diff -u --recursive --new-file v2.2.11/linux/CREDITS linux/CREDITS --- v2.2.11/linux/CREDITS Mon Aug 9 16:05:54 1999 +++ linux/CREDITS Wed Aug 25 17:29:45 1999 @@ -689,14 +689,11 @@ S: USA N: Paul Gortmaker -E: gpg109@rsphy1.anu.edu.au -W: http://rsphy1.anu.edu.au/~gpg109 -D: Real Time Clock driver author. -D: 8390 net driver hacker (ne2000, wd8013, smc-ultra, 3c503, etc.) -D: Ethernet-HOWTO and BootPrompt-HOWTO author. -D: Added many new CONFIG options (modules, ramdisk, generic-serial, etc.) -D: Implemented 1st "official" kernel thread (moved user bdflush to kflushd) -D: Various other random hacks, patches and utilities. +E: p_gortmaker@yahoo.com +D: Author of RTC driver & several net drivers, Ethernet & BootPrompt Howto. +D: Made support for modules, ramdisk, generic-serial, etc. optional. +D: Transformed old user space bdflush into 1st kernel thread - kflushd. +D: Many other patches, documentation files, mini kernels, utilities, ... N: John E. Gotts E: jgotts@engin.umich.edu @@ -978,6 +975,16 @@ S: Chapel Hill, North Carolina 27514-4818 S: USA +N: Dave Jones +E: dave@powertweak.com +W: http://linux.powertweak.com +D: Centaur/IDT Winchip/Winchip 2 tweaks +D: Misc clean ups and other random hacking. +S: 40, Heol Edward Lewis, +S: Gelligaer, Hengoed, +S: Mid Glamorgan, CF82 8EJ, +S: Wales, United Kingdom + N: Bernhard Kaindl E: bkaindl@netway.at E: edv@bartelt.via.at @@ -988,6 +995,15 @@ S: 8103 Rein S: Austria +N: Jan Kara +E: jack@atrey.karlin.mff.cuni.cz +D: Quota fixes for 2.2 kernel +D: Few other fixes in filesystem area (isofs, loopback) +W: http://atrey.karlin.mff.cuni.cz/~jack/ +S: Krosenska' 543 +S: 181 00 Praha 8 +S: Czech Republic + N: Jan "Yenya" Kasprzak E: kas@fi.muni.cz D: Author of the COSA/SRP sync serial board driver. @@ -1562,6 +1578,13 @@ S: Demonstratsii 8-382 S: Tula 300000 S: Russia + +N: Johnnie Peters +E: jpeters@phx.mcd.mot.com +D: Motorola PowerPC changes for PReP +S: 2900 S. Diable Way +S: Tempe, Arizona 85282 +S: USA N: Kirk Petersen E: kirk@speakeasy.org diff -u --recursive --new-file v2.2.11/linux/Documentation/00-INDEX linux/Documentation/00-INDEX --- v2.2.11/linux/Documentation/00-INDEX Thu Apr 29 11:53:41 1999 +++ linux/Documentation/00-INDEX Wed Aug 25 17:29:45 1999 @@ -25,10 +25,14 @@ - notes, and "To Fix" list for multi-processor Linux. (see smp.tex) VGA-softcursor.txt - how to change your VGA cursor from a blinking underscore. +arm/ + - directory with info about Linux on the ARM architecture. binfmt_misc.txt - info on the kernel support for extra binary formats. cdrom/ - directory with information on the CD-ROM drivers that Linux has. +cpqarray.txt + - info on using Compaq's SMART2 Intelligent Disk Array Controllers. devices.tex - TeX source listing of all the nodes in /dev/ with major minor #'s devices.txt @@ -39,12 +43,16 @@ - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. exception.txt - how Linux v2.2 handles exceptions without verify_area etc. +fb/ + - directory with info on the frame buffer graphics abstraction layer. filesystems/ - directory with info on the various filesystems that Linux supports. ftape.txt - notes about the floppy tape device driver hayes-esp.txt - info on using the Hayes ESP serial driver. +i386/ + - directory with info about Linux on the intel ix86 architecture. ide.txt - important info for users of ATA devices (IDE/EIDE disks and CD-ROMS) initrd.txt @@ -57,10 +65,18 @@ - info on the in-kernel binary support for Java(tm) joystick.txt - info on using joystick devices (and driver) with Linux. +joystick-api.txt + - API specification for applications that will be using the joystick. +joystick-parport.txt + - info on how to hook joysticks/gamepads to the parallel port. kbuild/ - directory with info about the kernel build process +kernel-docs.txt + - listing of various WWW + books that document kernel internals. +kernel-parameters.txt + - summary listing of command line / boot prompt args for the kernel. kmod.txt - - - info on the kernel module loader/unloader (kerneld replacement) + - info on the kernel module loader/unloader (kerneld replacement). locks.txt - info on file locking implementations, flock() vs. fcntl(), etc. logo.gif @@ -79,6 +95,8 @@ - info on boot arguments for the multiple devices driver memory.txt - info on typical Linux memory problems. +mkdev.ida + - script to make /dev entries for Intelligent Disk Array Controllers. modules.txt - short guide on how to make kernel parts into loadable modules mtrr.txt @@ -101,16 +119,22 @@ - info and sample code for using with the PC Watchdog reset card. powerpc/ - directory with info on using Linux with the PowerPC. +proc.txt + - detailed info on Linux's /proc filesystem. ramdisk.txt - short guide on how to set up and use the RAM disk. riscom8.txt - notes on using the RISCom/8 multi-port serial driver. rtc.txt - notes on how to use the Real Time Clock (aka CMOS clock) driver. +scsi-generic.txt + - info on the sg driver for generic (non-disk/CD/tape) SCSI devices. scsi.txt - short blurb on using SCSI support as a module. serial-console.txt - how to set up Linux with a serial line console as the default. +sgi-visws.txt + - short blurb on the SGI Visual Workstations. smart-config.txt - description of the Smart Config makefile feature. smp.tex @@ -127,6 +151,8 @@ - info on using the Stallion multiport serial driver. svga.txt - short guide on selecting video modes at boot via VGA BIOS. +sx.txt + - info on the Specialix SX/SI multiport serial driver. sysctl/ - directory with info on the /proc/sys/* files sysrq.txt @@ -135,6 +161,8 @@ - how to use name translation to ease use of diskless systems. unicode.txt - info on the Unicode character/font mapping used in Linux. +video4linux/ + - directory with info regarding video/TV/radio cards and linux. watchdog.txt - how to auto-reboot Linux if it has "fallen and can't get up". ;-) xterm-linux.xpm diff -u --recursive --new-file v2.2.11/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.2.11/linux/Documentation/Configure.help Mon Aug 9 16:05:54 1999 +++ linux/Documentation/Configure.help Wed Aug 25 17:29:45 1999 @@ -688,6 +688,17 @@ It's pretty unlikely that you have one of these: say N. +Mylex DAC960/DAC1100 PCI RAID Controller support +CONFIG_BLK_DEV_DAC960 + This driver adds support for the Mylex DAC960, AcceleRAID, and + eXtremeRAID PCI RAID controllers. See README.DAC960 for further + information about this driver. + + 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 DAC960.o. + Parallel port IDE device support CONFIG_PARIDE There are many external CD-ROM and disk devices that connect through @@ -2827,8 +2838,7 @@ 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. + about IPv6 under Linux read 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. You will still be able to do regular @@ -3248,6 +3258,13 @@ ### Don't know what's going on here. ### # +YAM driver for AX.25 +CONFIG_YAM + Support for the YAM modem on serial port. 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. + BAYCOM picpar and par96 driver for AX.25 CONFIG_BAYCOM_PAR This is a driver for Baycom style simple amateur radio modems that @@ -4595,8 +4612,8 @@ AMI MegaRAID support CONFIG_SCSI_MEGARAID - This driver supports the AMI MegaRAID 428 and 438 (and maybe 466) - SCSI host adapters. + This driver supports the AMI MegaRAID 418, 428, 438, 466, 762, 490 + and 467 SCSI host adapters. 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), @@ -5765,6 +5782,25 @@ say M here and read Documentation/modules.txt. This is recommended. The module will be called yellowfin.o. +General Instruments Surfboard 1000 +CONFIG_NET_SB1000 + This is a driver for the General Instrument SURFboard 1000 internal cable + modem. This is an ISA card which is used by a number of cable TV companies + to provide cable modem access. It's a one-way downstream-only cable modem, + meaning that your upstream net link is provided by your regular phone modem. + + At present this driver only compiles as a module, so say M here if you + have this card. Then read Documentation/networking/README.sb1000 for + information on how to use this module, as it needs special ppp scripts for + establishing a connection. Further documentation and the necessary scripts + can be found at: + + http://www.jacksonville.net/~fventuri/ + http://home.adelphia.net/~siglercm/sb1000.html + http://linuxpower.cx/~cable/ + + If you don't have this card, of course say N. + Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support CONFIG_ACENIC Say Y here if you have an Alteon AceNIC or 3Com 3C985 PCI Gigabit @@ -7254,11 +7290,12 @@ If unsure, say N. -System V and Coherent filesystem support +System V, Version 7 and Coherent filesystem support CONFIG_SYSV_FS SCO, Xenix and Coherent are commercial Unix systems for Intel - machines. Saying Y here would allow you to read to and write from - their floppies and hard disk partitions. + machines, and Version 7 was used on the DEC PDP-11. Saying Y here + would allow you to read to and write from their floppies and hard + disk partitions. If you have floppies or hard disk partitions like that, it is likely that they contain binaries from those other Unix systems; in order @@ -7943,6 +7980,16 @@ letters that were missing in Latin 4 to cover the entire Nordic area. +nls iso8859-14 +CONFIG_NLS_ISO8859_14 + 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 8 character + set, which adds the last accented vowels for Welsh (and Manx Gaelic) + that were missing in Latin 1. http://linux.speech.cymru.org/ + has further information. + nls iso8859-15 CONFIG_NLS_ISO8859_15 If you want to display filenames with native language characters @@ -9753,6 +9800,12 @@ you compiled aedsp16.o as a module you can specify this parameter as 'mpu_irq=NN'. +SGI Visual Workstation on-board audio +CONFIG_SOUND_VWSND + Say Y or M if you have an SGI Visual Workstation and you want to + be able to use its on-board audio. Read Documentation/sound/visws + for more info on this driver's capabilities. + Ensoniq ES1370 based PCI sound cards CONFIG_SOUND_ES1370 Say Y or M if you have a PCI sound card utilizing the Ensoniq @@ -9791,6 +9844,15 @@ Leave the default 200 unless you have a joystick not attached to your sound card. +ESS Solo1 based PCI sound cards (eg. SC1938) +CONFIG_SOUND_ESSSOLO1 + Say Y or M if you have a PCI sound card utilizing the ESS Technology + Solo1 chip. To find out if your sound card uses a + Solo1 chip without removing your computer's cover, use + lspci -n and look for the PCI ID 125D:1969. This driver + differs slightly from OSS/Free, so PLEASE READ + Documentation/sound/solo1. + S3 SonicVibes based PCI sound cards CONFIG_SOUND_SONICVIBES Say Y or M if you have a PCI sound card utilizing the S3 @@ -10180,6 +10242,30 @@ 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). + +Eicon.Diehl active card support +CONFIG_ISDN_DRV_EICON + Say Y here if you have an Eicon active ISDN card. In order to use + this card, additional firmware is necessary, which has to be loaded + into the card using the eiconctrl utility which is part of the latest + isdn4k-utils package. Please read the file + Documentation/isdn/README.eicon for more information. + +Eicon old-type card support +CONFIG_ISDN_DRV_EICON_ISA + Say Y here if you have an old-type Eicon active ISDN card. In order to + use this card, additional firmware is necessary, which has to be loaded + into the card using the eiconctrl utility which is part of the latest + isdn4k-utils package. Please read the file + Documentation/isdn/README.eicon for more information. + +Support AT-Fax Class 2 commands +CONFIG_ISDN_TTY_FAX + If you say Y here, the modem-emulator will support a subset of the + Fax Class 2 commands. Using a getty with fax-support + (mgetty+sendfax, hylafax), you will be able to use your Linux box + as an ISDN-fax-machine. This must be supported by the lowlevel driver + also. See Documentation/isdn/README.fax for more information. AVM-B1 with CAPI2.0 support CONFIG_ISDN_DRV_AVMB1 diff -u --recursive --new-file v2.2.11/linux/Documentation/README.DAC960 linux/Documentation/README.DAC960 --- v2.2.11/linux/Documentation/README.DAC960 Wed Dec 31 16:00:00 1969 +++ linux/Documentation/README.DAC960 Wed Aug 25 17:29:45 1999 @@ -0,0 +1,717 @@ + Mylex DAC960/DAC1100 PCI RAID Controller Driver for Linux + + Version 2.2.4 for Linux 2.2.11 + Version 2.0.4 for Linux 2.0.37 + + PRODUCTION RELEASE + + 23 August 1999 + + Leonard N. Zubkoff + Dandelion Digital + lnz@dandelion.com + + Copyright 1998-1999 by Leonard N. Zubkoff + + + INTRODUCTION + +Mylex, Inc. designs and manufactures a variety of high performance PCI RAID +controllers. Mylex Corporation is located at 34551 Ardenwood Blvd., Fremont, +California 94555, USA and can be reached at 510/796-6100 or on the World Wide +Web at http://www.mylex.com. Mylex RAID Technical Support can be reached by +electronic mail at support@mylex.com (for eXtremeRAID 1100 and older DAC960 +models) or techsup@mylex.com (for AcceleRAID models), by voice at 510/608-2400, +or by FAX at 510/745-7715. Contact information for offices in Europe and Japan +is available on the Web site. + +The latest information on Linux support for DAC960 PCI RAID Controllers, as +well as the most recent release of this driver, will always be available from +my Linux Home Page at URL "http://www.dandelion.com/Linux/". The Linux DAC960 +driver supports all current DAC960 PCI family controllers including the +AcceleRAID models, as well as the eXtremeRAID 1100; see below for a complete +list. For simplicity, in most places this documentation refers to DAC960 +generically rather than explicitly listing all the models. + +Bug reports should be sent via electronic mail to "lnz@dandelion.com". Please +include with the bug report the complete configuration messages reported by the +driver at startup, along with any subsequent system messages relevant to the +controller's operation, and a detailed description of your system's hardware +configuration. + +Please consult the DAC960 RAID controller documentation for detailed +information regarding installation and configuration of the controllers. This +document primarily provides information specific to the Linux DAC960 support. + + + DRIVER FEATURES + +The DAC960 RAID controllers are supported solely as high performance RAID +controllers, not as interfaces to arbitrary SCSI devices. The Linux DAC960 +driver operates at the block device level, the same level as the SCSI and IDE +drivers. Unlike other RAID controllers currently supported on Linux, the +DAC960 driver is not dependent on the SCSI subsystem, and hence avoids all the +complexity and unnecessary code that would be associated with an implementation +as a SCSI driver. The DAC960 driver is designed for as high a performance as +possible with no compromises or extra code for compatibility with lower +performance devices. The DAC960 driver includes extensive error logging and +online configuration management capabilities. Except for initial configuration +of the controller and adding new disk drives, most everything can be handled +from Linux while the system is operational. + +The DAC960 driver is architected to support up to 8 controllers per system. +Each DAC960 controller can support up to 15 disk drives per channel, for a +maximum of 45 drives on a three channel controller. The drives installed on a +controller are divided into one or more "Drive Groups", and then each Drive +Group is subdivided further into 1 to 32 "Logical Drives". Each Logical Drive +has a specific RAID Level and caching policy associated with it, and it appears +to Linux as a single block device. Logical Drives are further subdivided into +up to 7 partitions through the normal Linux and PC disk partitioning schemes. +Logical Drives are also known as "System Drives", and Drive Groups are also +called "Packs". Both terms are in use in the Mylex documentation; I have +chosen to standardize on the more generic "Logical Drive" and "Drive Group". + +DAC960 RAID disk devices are named in the style of the Device File System +(DEVFS). The device corresponding to Logical Drive D on Controller C is +referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1 +through /dev/rd/cCdDp7. For example, partition 3 of Logical Drive 5 on +Controller 2 is referred to as /dev/rd/c2d5p3. Note that unlike with SCSI +disks the device names will not change in the event of a disk drive failure. +The DAC960 driver is assigned major numbers 48 - 55 with one major number per +controller. The 8 bits of minor number are divided into 5 bits for the Logical +Drive and 3 bits for the partition. + + + SUPPORTED DAC960/DAC1100 PCI RAID CONTROLLERS + +The following list comprises the supported DAC960 and DAC1100 PCI RAID +Controllers as of the date of this document. It is recommended that anyone +purchasing a Mylex PCI RAID Controller not in the following table contact the +author beforehand to verify that it is or will be supported. + +eXtremeRAID 1100 (DAC1164P) + 3 Wide Ultra-2/LVD SCSI channels + 233MHz StrongARM SA 110 Processor + 64 Bit PCI (backward compatible with 32 Bit PCI slots) + 16MB/32MB/64MB Parity SDRAM Memory with Battery Backup + +AcceleRAID 250 (DAC960PTL1) + Uses onboard Symbios SCSI chips on certain motherboards + Also includes one onboard Wide Ultra-2/LVD SCSI Channel + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +AcceleRAID 200 (DAC960PTL0) + Uses onboard Symbios SCSI chips on certain motherboards + Includes no onboard SCSI Channels + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +AcceleRAID 150 (DAC960PRL) + Uses onboard Symbios SCSI chips on certain motherboards + Also includes one onboard Wide Ultra-2/LVD SCSI Channel + 33MHz Intel i960RP RISC Processor + 4MB Parity EDO Memory + +DAC960PJ 1/2/3 Wide Ultra SCSI-3 Channels + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +DAC960PG 1/2/3 Wide Ultra SCSI-3 Channels + 33MHz Intel i960RP RISC Processor + 4MB/8MB ECC EDO Memory + +DAC960PU 1/2/3 Wide Ultra SCSI-3 Channels + Intel i960CF RISC Processor + 4MB/8MB EDRAM or 2MB/4MB/8MB/16MB/32MB DRAM Memory + +DAC960PD 1/2/3 Wide Fast SCSI-2 Channels + Intel i960CF RISC Processor + 4MB/8MB EDRAM or 2MB/4MB/8MB/16MB/32MB DRAM Memory + +DAC960PL 1/2/3 Wide Fast SCSI-2 Channels + Intel i960 RISC Processor + 2MB/4MB/8MB/16MB/32MB DRAM Memory + +For the eXtremeRAID 1100, firmware version 5.06-0-52 or above is required. + +For the AcceleRAID 250, 200, and 150, firmware version 4.06-0-57 or above is +required. + +For the DAC960PJ and DAC960PG, firmware version 4.06-0-00 or above is required. + +For the DAC960PU, DAC960PD, and DAC960PL, firmware version 3.51-0-04 or above +is required. + +Note that earlier revisions of the DAC960PU, DAC960PD, and DAC960PL controllers +were delivered with version 2.xx firmware. Version 2.xx firmware is not +supported by this driver and no support is envisioned. Contact Mylex RAID +Technical Support to inquire about upgrading controllers with version 2.xx +firmware to version 3.51-0-04. Upgrading to version 3.xx firmware requires +installation of higher capacity Flash ROM chips, and not all DAC960PD and +DAC960PL controllers can be upgraded. + +Please note that not all SCSI disk drives are suitable for use with DAC960 +controllers, and only particular firmware versions of any given model may +actually function correctly. Similarly, not all motherboards have a BIOS that +properly initializes the AcceleRAID 250, AcceleRAID 200, AcceleRAID 150, +DAC960PJ, and DAC960PG because the Intel i960RD/RP is a multi-function device. +If in doubt, contact Mylex RAID Technical Support (support@mylex.com) to verify +compatibility. Mylex makes available a hard disk compatibility list by FTP at +ftp://ftp.mylex.com/pub/dac960/diskcomp.html. + + + DRIVER INSTALLATION + +This distribution was prepared for Linux kernel version 2.2.11 or 2.0.37. + +To install the DAC960 RAID driver, you may use the following commands, +replacing "/usr/src" with wherever you keep your Linux kernel source tree: + + cd /usr/src + tar -xvzf DAC960-2.2.4.tar.gz (or DAC960-2.0.4.tar.gz) + mv README.DAC960 linux/Documentation + mv DAC960.[ch] linux/drivers/block + patch -p0 < DAC960.patch + cd linux + make config + make depend + make bzImage (or zImage) + +Then install "arch/i386/boot/bzImage" or "arch/i386/boot/zImage" as your +standard kernel, run lilo if appropriate, and reboot. + +To create the necessary devices in /dev, the "make_rd" script included in +"DAC960-Utilities.tar.gz" from http://www.dandelion.com/Linux/ may be used. +LILO 21 and FDISK v2.9 include DAC960 support; also included in this archive +are patches to LILO 20 and FDISK v2.8 that add DAC960 support, along with +statically linked executables of LILO and FDISK. This modified version of LILO +will allow booting from a DAC960 controller and/or mounting the root file +system from a DAC960. + +Red Hat Linux 6.0 and SuSE Linux 6.1 include support for Mylex PCI RAID +controllers. Installing directly onto a DAC960 may be problematic from other +Linux distributions until their installation utilities are updated. + + + INSTALLATION NOTES + +Before installing Linux or adding DAC960 logical drives to an existing Linux +system, the controller must first be configured to provide one or more logical +drives using the BIOS Configuration Utility or DACCF. Please note that since +there are only at most 6 usable partitions on each logical drive, systems +requiring more partitions should subdivide a drive group into multiple logical +drives, each of which can have up to 6 partitions. Also, note that with large +disk arrays it is advisable to enable the 8GB BIOS Geometry (255/63) rather +than accepting the default 2GB BIOS Geometry (128/32); failing to so do will +cause the logical drive geometry to have more than 65535 cylinders which will +make it impossible for FDISK to be used properly. The 8GB BIOS Geometry can be +enabled by configuring the DAC960 BIOS, which is accessible via Alt-M during +the BIOS initialization sequence. + +For maximum performance and the most efficient E2FSCK performance, it is +recommended that EXT2 file systems be built with a 4KB block size and 16 block +stride to match the DAC960 controller's 64KB default stripe size. The command +"mke2fs -b 4096 -R stride=16 " is appropriate. Unless there will be a +large number of small files on the file systems, it is also beneficial to add +the "-i 16384" option to increase the bytes per inode parameter thereby +reducing the file system metadata. Finally, on systems that will only be run +with Linux 2.2 or later kernels it is beneficial to enable sparse superblocks +with the "-s 1" option. + + + DAC960 ANNOUNCEMENTS MAILING LIST + +The DAC960 Announcements Mailing List provides a forum for informing Linux +users of new driver releases and other announcements regarding Linux support +for DAC960 PCI RAID Controllers. To join the mailing list, send a message to +"dac960-announce-request@dandelion.com" with the line "subscribe" in the +message body. + + + CONTROLLER CONFIGURATION AND STATUS MONITORING + +The DAC960 RAID controllers running firmware 4.06 or above include a Background +Initialization facility so that system downtime is minimized both for initial +installation and subsequent configuration of additional storage. The BIOS +Configuration Utility (accessible via Alt-R during the BIOS initialization +sequence) is used to quickly configure the controller, and then the logical +drives that have been created are available for immediate use even while they +are still being initialized by the controller. The primary need for online +configuration and status monitoring is then to avoid system downtime when disk +drives fail and must be replaced. Mylex's online monitoring and configuration +utilities are being ported to Linux and will become available at some point in +the future. Note that with a SAF-TE (SCSI Accessed Fault-Tolerant Enclosure) +enclosure, the controller is able to rebuild failed drives automatically as +soon as a drive replacement is made available. + +The primary interfaces for controller configuration and status monitoring are +special files created in the /proc/rd/... hierarchy along with the normal +system console logging mechanism. Whenever the system is operating, the DAC960 +driver queries each controller for status information every 10 seconds, and +checks for additional conditions every 60 seconds. The initial status of each +controller is always available for controller N in /proc/rd/cN/initial_status, +and the current status as of the last status monitoring query is available in +/proc/rd/cN/current_status. In addition, status changes are also logged by the +driver to the system console and will appear in the log files maintained by +syslog. The progress of asynchronous rebuild or consistency check operations +is also available in /proc/rd/cN/current_status, and progress messages are +logged to the system console at most every 60 seconds. + +Starting with the 2.2.3/2.0.3 versions of the driver, the status information +available in /proc/rd/cN/initial_status and /proc/rd/cN/current_status has been +augmented to include the vendor, model, revision, and serial number (if +available) for each physical device found connected to the controller: + +***** DAC960 RAID Driver Version 2.2.3 of 19 August 1999 ***** +Copyright 1998-1999 by Leonard N. Zubkoff +Configuring Mylex DAC960PRL PCI RAID Controller + Firmware Version: 4.07-0-07, Channels: 1, Memory Size: 16MB + PCI Bus: 1, Device: 4, Function: 1, I/O Address: Unassigned + PCI Address: 0xFE300000 mapped at 0xA0800000, IRQ Channel: 21 + Controller Queue Depth: 128, Maximum Blocks per Command: 128 + Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33 + Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63 + SAF-TE Enclosure Management Enabled + Physical Devices: + 0:0 Vendor: IBM Model: DRVS09D Revision: 0270 + Serial Number: 68016775HA + Disk Status: Online, 17928192 blocks + 0:1 Vendor: IBM Model: DRVS09D Revision: 0270 + Serial Number: 68004E53HA + Disk Status: Online, 17928192 blocks + 0:2 Vendor: IBM Model: DRVS09D Revision: 0270 + Serial Number: 13013935HA + Disk Status: Online, 17928192 blocks + 0:3 Vendor: IBM Model: DRVS09D Revision: 0270 + Serial Number: 13016897HA + Disk Status: Online, 17928192 blocks + 0:4 Vendor: IBM Model: DRVS09D Revision: 0270 + Serial Number: 68019905HA + Disk Status: Online, 17928192 blocks + 0:5 Vendor: IBM Model: DRVS09D Revision: 0270 + Serial Number: 68012753HA + Disk Status: Online, 17928192 blocks + 0:6 Vendor: ESG-SHV Model: SCA HSBP M6 Revision: 0.61 + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 89640960 blocks, Write Thru + No Rebuild or Consistency Check in Progress + +To simplify the monitoring process for custom software, the special file +/proc/rd/status returns "OK" when all DAC960 controllers in the system are +operating normally and no failures have occurred, or "ALERT" if any logical +drives are offline or critical or any non-standby physical drives are dead. + +Configuration commands for controller N are available via the special file +/proc/rd/cN/user_command. A human readable command can be written to this +special file to initiate a configuration operation, and the results of the +operation can then be read back from the special file in addition to being +logged to the system console. The shell command sequence + + echo "" > /proc/rd/c0/user_command + cat /proc/rd/c0/user_command + +is typically used to execute configuration commands. The configuration +commands are: + + flush-cache + + The "flush-cache" command flushes the controller's cache. The system + automatically flushes the cache at shutdown or if the driver module is + unloaded, so this command is only needed to be certain a write back cache + is flushed to disk before the system is powered off by a command to a UPS. + Note that the flush-cache command also stops an asynchronous rebuild or + consistency check, so it should not be used except when the system is being + halted. + + kill : + + The "kill" command marks the physical drive : as DEAD. + This command is provided primarily for testing, and should not be used + during normal system operation. + + make-online : + + The "make-online" command changes the physical drive : + from status DEAD to status ONLINE. In cases where multiple physical drives + have been killed simultaneously, this command may be used to bring them + back online, after which a consistency check is advisable. + + Warning: make-online should only be used on a dead physical drive that is + an active part of a drive group, never on a standby drive. + + make-standby : + + The "make-standby" command changes physical drive : + from status DEAD to status STANDBY. It should only be used in cases where + a dead drive was replaced after an automatic rebuild was performed onto a + standby drive. It cannot be used to add a standby drive to the controller + configuration if one was not created initially; the BIOS Configuration + Utility must be used for that currently. + + rebuild : + + The "rebuild" command initiates an asynchronous rebuild onto physical drive + :. It should only be used when a dead drive has been + replaced. + + check-consistency + + The "check-consistency" command initiates an asynchronous consistency check + of with automatic restoration. It can be used + whenever it is desired to verify the consistency of the redundancy + information. + + cancel-rebuild + cancel-consistency-check + + The "cancel-rebuild" and "cancel-consistency-check" commands cancel any + rebuild or consistency check operations previously initiated. + + + EXAMPLE I - DRIVE FAILURE WITHOUT A STANDBY DRIVE + +The following annotated logs demonstrate the controller configuration and and +online status monitoring capabilities of the Linux DAC960 Driver. The test +configuration comprises 6 1GB Quantum Atlas I disk drives on two channels of a +DAC960PJ controller. The physical drives are configured into a single drive +group without a standby drive, and the drive group has been configured into two +logical drives, one RAID-5 and one RAID-6. Note that these logs are from an +earlier version of the driver and the messages have changed somewhat with newer +releases, but the functionality remains similar. First, here is the current +status of the RAID configuration: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status +***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 ***** +Copyright 1998-1999 by Leonard N. Zubkoff +Configuring Mylex DAC960PJ PCI RAID Controller + Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB + PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned + PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9 + Controller Queue Depth: 128, Maximum Blocks per Command: 128 + Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33 + Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63 + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 3305472 blocks, Write Thru + No Rebuild or Consistency Check in Progress + +gwynedd:/u/lnz# cat /proc/rd/status +OK + +The above messages indicate that everything is healthy, and /proc/rd/status +returns "OK" indicating that there are no problems with any DAC960 controller +in the system. For demonstration purposes, while I/O is active Physical Drive +1:1 is now disconnected, simulating a drive failure. The failure is noted by +the driver within 10 seconds of the controller's having detected it, and the +driver logs the following console status messages indicating that Logical +Drives 0 and 1 are now CRITICAL as a result of Physical Drive 1:1 being DEAD: + +DAC960#0: Physical Drive 1:2 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02 +DAC960#0: Physical Drive 1:3 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02 +DAC960#0: Physical Drive 1:1 killed because of timeout on SCSI command +DAC960#0: Physical Drive 1:1 is now DEAD +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL + +The Sense Keys logged here are just Check Condition / Unit Attention conditions +arising from a SCSI bus reset that is forced by the controller during its error +recovery procedures. Concurrently with the above, the driver status available +from /proc/rd also reflects the drive failure. The status message in +/proc/rd/status has changed from "OK" to "ALERT": + +gwynedd:/u/lnz# cat /proc/rd/status +ALERT + +and /proc/rd/c0/current_status has been updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Dead, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru + No Rebuild or Consistency Check in Progress + +Since there are no standby drives configured, the system can continue to access +the logical drives in a performance degraded mode until the failed drive is +replaced and a rebuild operation completed to restore the redundancy of the +logical drives. Once Physical Drive 1:1 is replaced with a properly +functioning drive, or if the physical drive was killed without having failed +(e.g., due to electrical problems on the SCSI bus), the user can instruct the +controller to initiate a rebuild operation onto the newly replaced drive: + +gwynedd:/u/lnz# echo "rebuild 1:1" > /proc/rd/c0/user_command +gwynedd:/u/lnz# cat /proc/rd/c0/user_command +Rebuild of Physical Drive 1:1 Initiated + +The echo command instructs the controller to initiate an asynchronous rebuild +operation onto Physical Drive 1:1, and the status message that results from the +operation is then available for reading from /proc/rd/c0/user_command, as well +as being logged to the console by the driver. + +Within 10 seconds of this command the driver logs the initiation of the +asynchronous rebuild operation: + +DAC960#0: Rebuild of Physical Drive 1:1 Initiated +DAC960#0: Physical Drive 1:1 Error Log: Sense Key = 6, ASC = 29, ASCQ = 01 +DAC960#0: Physical Drive 1:1 is now WRITE-ONLY +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 1% completed + +and /proc/rd/c0/current_status is updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Write-Only, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru + Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 6% completed + +As the rebuild progresses, the current status in /proc/rd/c0/current_status is +updated every 10 seconds: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Write-Only, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru + Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 15% completed + +and every minute a progress message is logged to the console by the driver: + +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 32% completed +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 63% completed +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 94% completed +DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 94% completed + +Finally, the rebuild completes successfully. The driver logs the status of the +logical and physical drives and the rebuild completion: + +DAC960#0: Rebuild Completed Successfully +DAC960#0: Physical Drive 1:1 is now ONLINE +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE + +/proc/rd/c0/current_status is updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 5498880 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 3305472 blocks, Write Thru + Rebuild Completed Successfully + +and /proc/rd/status indicates that everything is healthy once again: + +gwynedd:/u/lnz# cat /proc/rd/status +OK + + + EXAMPLE II - DRIVE FAILURE WITH A STANDBY DRIVE + +The following annotated logs demonstrate the controller configuration and and +online status monitoring capabilities of the Linux DAC960 Driver. The test +configuration comprises 6 1GB Quantum Atlas I disk drives on two channels of a +DAC960PJ controller. The physical drives are configured into a single drive +group with a standby drive, and the drive group has been configured into two +logical drives, one RAID-5 and one RAID-6. Note that these logs are from an +earlier version of the driver and the messages have changed somewhat with newer +releases, but the functionality remains similar. First, here is the current +status of the RAID configuration: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status +***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 ***** +Copyright 1998-1999 by Leonard N. Zubkoff +Configuring Mylex DAC960PJ PCI RAID Controller + Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB + PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned + PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9 + Controller Queue Depth: 128, Maximum Blocks per Command: 128 + Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33 + Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63 + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Online, 2201600 blocks + 1:3 - Disk: Standby, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru + No Rebuild or Consistency Check in Progress + +gwynedd:/u/lnz# cat /proc/rd/status +OK + +The above messages indicate that everything is healthy, and /proc/rd/status +returns "OK" indicating that there are no problems with any DAC960 controller +in the system. For demonstration purposes, while I/O is active Physical Drive +1:2 is now disconnected, simulating a drive failure. The failure is noted by +the driver within 10 seconds of the controller's having detected it, and the +driver logs the following console status messages: + +DAC960#0: Physical Drive 1:1 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02 +DAC960#0: Physical Drive 1:3 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02 +DAC960#0: Physical Drive 1:2 killed because of timeout on SCSI command +DAC960#0: Physical Drive 1:2 is now DEAD +DAC960#0: Physical Drive 1:2 killed because it was removed +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL + +Since a standby drive is configured, the controller automatically begins +rebuilding onto the standby drive: + +DAC960#0: Physical Drive 1:3 is now WRITE-ONLY +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 4% completed + +Concurrently with the above, the driver status available from /proc/rd also +reflects the drive failure and automatic rebuild. The status message in +/proc/rd/status has changed from "OK" to "ALERT": + +gwynedd:/u/lnz# cat /proc/rd/status +ALERT + +and /proc/rd/c0/current_status has been updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Dead, 2201600 blocks + 1:3 - Disk: Write-Only, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 2754560 blocks, Write Thru + Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 4% completed + +As the rebuild progresses, the current status in /proc/rd/c0/current_status is +updated every 10 seconds: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Dead, 2201600 blocks + 1:3 - Disk: Write-Only, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Critical, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Critical, 2754560 blocks, Write Thru + Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 40% completed + +and every minute a progress message is logged on the console by the driver: + +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 40% completed +DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 76% completed +DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 66% completed +DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 84% completed + +Finally, the rebuild completes successfully. The driver logs the status of the +logical and physical drives and the rebuild completion: + +DAC960#0: Rebuild Completed Successfully +DAC960#0: Physical Drive 1:3 is now ONLINE +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE + +/proc/rd/c0/current_status is updated: + +***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 ***** +Copyright 1998-1999 by Leonard N. Zubkoff +Configuring Mylex DAC960PJ PCI RAID Controller + Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB + PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned + PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9 + Controller Queue Depth: 128, Maximum Blocks per Command: 128 + Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33 + Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63 + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Dead, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru + Rebuild Completed Successfully + +and /proc/rd/status indicates that everything is healthy once again: + +gwynedd:/u/lnz# cat /proc/rd/status +OK + +Note that the absence of a viable standby drive does not create an "ALERT" +status. Once dead Physical Drive 1:2 has been replaced, the controller must be +told that this has occurred and that the newly replaced drive should become the +new standby drive: + +gwynedd:/u/lnz# echo "make-standby 1:2" > /proc/rd/c0/user_command +gwynedd:/u/lnz# cat /proc/rd/c0/user_command +Make Standby of Physical Drive 1:2 Succeeded + +The echo command instructs the controller to make Physical Drive 1:2 into a +standby drive, and the status message that results from the operation is then +available for reading from /proc/rd/c0/user_command, as well as being logged to +the console by the driver. Within 60 seconds of this command the driver logs: + +DAC960#0: Physical Drive 1:2 Error Log: Sense Key = 6, ASC = 29, ASCQ = 01 +DAC960#0: Physical Drive 1:2 is now STANDBY +DAC960#0: Make Standby of Physical Drive 1:2 Succeeded + +and /proc/rd/c0/current_status is updated: + +gwynedd:/u/lnz# cat /proc/rd/c0/current_status + ... + Physical Devices: + 0:1 - Disk: Online, 2201600 blocks + 0:2 - Disk: Online, 2201600 blocks + 0:3 - Disk: Online, 2201600 blocks + 1:1 - Disk: Online, 2201600 blocks + 1:2 - Disk: Standby, 2201600 blocks + 1:3 - Disk: Online, 2201600 blocks + Logical Drives: + /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru + /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru + Rebuild Completed Successfully diff -u --recursive --new-file v2.2.11/linux/Documentation/computone.txt linux/Documentation/computone.txt --- v2.2.11/linux/Documentation/computone.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/computone.txt Wed Aug 25 17:29:45 1999 @@ -0,0 +1,343 @@ + +Computone Intelliport II/Plus Multiport Serial Driver +----------------------------------------------------- + +Release Notes For Linux Kernel 2.2 and higher. +These notes are for the drivers which have already been integrated into the +kernel and have been tested on Linux kernels 2.0, 2.2, and 2.3. + +Version: 1.2.4 +Date: 08/04/99 +Author: Andrew Manison +Testing: larryg@computone.com +Support: support@computone.com +Fixes and Updates: Doug McNash +Proc Filesystem and Kernel Integration: Mike Warfield + + +This file assumes that you are using the Computone drivers which are +integrated into the kernel sources. For updating the drivers or installing +drivers into kernels which do not already have Computone drivers, please +refer to the instructions in the README.computone file in the driver patch. + + +1. INTRODUCTION + +This driver supports the entire family of Intelliport II/Plus controllers +with the exception of the MicroChannel controllers. It does not support +products previous to the Intelliport II. + +This driver was developed on the v2.0.x Linux tree and has been tested up +to v2.2.9; it will probably not work with earlier v1.X kernels,. + + +2. QUICK INSTALLATION + +Hardware - If you have an ISA card, find a free interrupt and io port. + List those in use with `cat /proc/interrupts` and + `cat /proc/ioports`. Set the card dip switches to a free + address. You may need to configure your BIOS to reserve an + irq for an ISA card. PCI and EISA parameters are set + automagically. Insert card into computer with the power off + before or after drivers installation. + + Note the hardware address from the Computone ISA cards installed into + the system. These are required for editing ip2.h or editing + /etc/config.modules, or for specification on the modprobe + command line. + +Software - + +Module installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (m) module for CONFIG_COMPUTONE under character + devices. CONFIG_PCI and CONFIG_MODULES also may need to be set. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h if needed + or + edit /etc/conf.modules (or /etc/modules.conf) if needed (module). + or both to match this setting. +f) Run "make dep" +g) Run "make modules" +h) Run "make modules_install" +i) Run "/sbin/depmod -a" +j) install driver using `modprobe ip2 ` (options listed below) +k) run ip2mkdev (either the script below or the binary version) + + +Kernel installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (y) kernel for CONFIG_COMPUTONE under character + devices. CONFIG_PCI may need to be set if you have PCI bus. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h +f) Run "make dep" +g) Run "make zImage" or whatever target you prefer. +h) mv /usr/src/linux/arch/i386/boot/zImage to /boot. +i) Add new config for this kernel into /etc/lilo.conf, run "lilo" + or copy to a floppy disk and boot from that floppy disk. +j) Reboot using this kernel +k) run ip2mkdev (either the script below or the binary version) + + +3. INSTALLATION + +Previously, the driver sources were packaged with a set of patch files +to update the character drivers' makefile and configuration file, and other +kernel source files. A build script (ip2build) was included which applies +the patches if needed, and build any utilities needed. +What you recieve may be a single patch file in conventional kernel +patch format build script. That form can also be applied by +running patch -p1 < ThePatchFile. Otherwise run ip2build. + +The driver can be installed as a module (recommended) or built into the +kernel. This is selected as for other drivers through the `make config` +command from the root of the Linux source tree. If the driver is built +into the kernel you will need to edit the file ip2.h to match the boards +you are installing. See that file for instructions. If the driver is +installed as a module the configuration can also be specified on the +modprobe command line as follows: + + modprobe ip2 irq=irq1,irq2,irq3,irq4 io=addr1,addr2,addr3,addr4 + +where irqnum is one of the valid Intelliport II interrupts (3,4,5,7,10,11, +12,15) and addr1-4 are the base addresses for up to four controllers. If +the irqs are not specified the driver uses the default in ip2/ip2.h (which +selects polled mode). If no base addresses are specified the defaults in +ip2.h are used. If you are autoloading the driver module with kerneld or +kmod the base addresses and interrupt number must also be set in ip2/ip2.h +and recompile or just insert and options line in /etc/modules.conf or both. +The options line is equivalent to the command line and takes precidence over +what is in ip2.h. + +/etc/modules.conf sample: + options ip2 io=1,0x328 irq=1,10 + alias char-major-71 ip2 + alias char-major-72 ip2 + alias char-major-73 ip2 + +equivelant ip2.h: +static ip2config_t ip2config = +{ + {1,10,0,0}, + { + 0x0001, // Board 0, ttyF0 - ttyF63 /* PCI card */ + 0x0328, // Board 1, ttyF64 - ttyF127 /* ISA card */ + 0x0000, // Board 2, ttyF128 - ttyF191 /* empty */ + 0x0000 // Board 3, ttyF192 - ttyF255 /* empty */ + } +}; + + +Note: Both io and irq should be updated to reflect YOUR system. An "io" + address of "1/2" indicates a PCI/EISA card in the board table. The + PCI or EISA irq will be assigned automatically. + +Specifying an invalid or in-use irq will default the driver into +running in polled mode for that card. If all irq entries are 0 then +all cards will operate in polled mode. + +If you select the driver as part of the kernel run : + + make depend + make zlilo (or whatever you do to create a bootable kernel) + +If you selected a module run : + + make modules && make modules_install + +The utility ip2mkdev (see 5 and 7 below) creates all the device nodes +required by the driver. For a device to be created it must be configured +in the driver and the board must be installed. Only devices corresponding +to real IntelliPort II ports are created. With multiple boards and expansion +boxes this will leave gaps in the sequence of device names. ip2mkdev uses +Linux tty naming conventions: ttyF0 - ttyF255 for normal devices, and +cuf0 - cuf255 for callout devices. + + +4. USING THE DRIVERS + +As noted above, the driver implements the ports in accordance with Linux +conventions, and the devices should be interchangeable with the standard +serial devices. (This is a key point for problem reporting: please make +sure that what you are trying do works on the ttySx/cuax ports first; then +tell us what went wrong with the ip2 ports!) + +Higher speeds can be obtained using the setserial utility which remaps +38,400 bps (extb) to 57,600 bps, 115,200 bps, or a custom speed. +Intelliport II installations using the PowerPort expansion module can +use the custom speed setting to select the highest speeds: 153,600 bps, +230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for +custom baud rate configuration is fixed at 921,600 for cards/expantion +modules with ST654's and 115200 for those with Cirrus CD1400's. This +corresponds to the maximum bit rates those chips are capable. +For example if the baud base is 921600 and the baud divisor is 18 then +the custom rate is 921600/18 = 51200 bps. See the setserial man page for +complete details. Of course if stty accepts the higher rates now you can +use that as well as the standard ioctls(). + + +5. ip2mkdev and assorted utilities... + +Several utilities, including the source for a binary ip2mkdev utility are +available under .../drivers/char/ip2. These can be build by changing to +that directory and typing "make" after the kernel has be built. If you do +not wish to compile the binary utilities, the shell script below can be +cut out and run as "ip2mkdev" to create the necessary device files. To +use the ip2mkdev script, you must have procfs enabled and the proc file +system mounted on /proc. + +6. NOTES + +This is a release version of the driver, but it is impossible to test it +in all configurations of Linux. If there is any anomalous behaviour that +does not match the standard serial port's behaviour please let us know. + + +7. ip2mkdev shell script + +===== Cut Here ===== +#!/bin/sh - + +# ip2mkdev +# +# Make or remove devices as needed for Computone Intelliport drivers +# +# First rule! If the dev file exists and you need it, don't mess +# with it. That prevents us from screwing up open ttys, ownership +# and permissions on a running system! +# +# This script will NOT remove devices that no longer exist because +# their board or interface box has been removed. If you want to get +# rid of them, you can manually do an "rm -f /dev/ttyF* /dev/cuaf*" +# before running this script, which will then recreate all the valid +# devices +# +# =mhw= +# Michael H. Warfield +# mhw@wittsend.com +# +if test ! -f /proc/tty/drivers +then + echo "\ +Unable to check driver status. +Make sure proc file system is mounted." + + exit 255 +fi + +if test ! -f /proc/tty/driver/ip2 +then + echo "\ +Unable to locate ip2 proc file. +Attempting to load driver" + + if insmod ip2 + then + if test ! -f /proc/tty/driver/ip2 + then + echo "\ +Unable to locate ip2 proc file after loading driver. +Driver initialization failure or driver version error. +" + exit 255 + fi + else + echo "Unable to load ip2 driver." + exit 255 + fi +fi + +# Ok... So we got the driver loaded and we can locate the procfs files. +# Next we need our major numbers. + +TTYMAJOR=`sed -e '/^ip2/!d' -e '/\/dev\/tty/!d' -e 's/.*tty.[ ]*\([0-9]*\)[ ]*.*/\1/' < /proc/tty/drivers` +CUAMAJOR=`sed -e '/^ip2/!d' -e '/\/dev\/cu/!d' -e 's/.*cu.[ ]*\([0-9]*\)[ ]*.*/\1/' < /proc/tty/drivers` +BRDMAJOR=`sed -e '/^Driver: /!d' -e 's/.*IMajor=\([0-9]*\)[ ]*.*/\1/' < /proc/tty/driver/ip2` + +echo "\ +TTYMAJOR = $TTYMAJOR +CUAMAJOR = $CUAMAJOR +BRDMAJOR = $BRDMAJOR +" + +# Ok... Now we should know our major numbers, if appropriate... +# Now we need our boards and start the device loops. + +grep '^Board [0-9]:' /proc/tty/driver/ip2 | while read token number type alltherest +do + # The test for blank "type" will catch the stats lead-in lines + # if they exist in the file + if test "$type" = "vacant" -o "$type" = "Vacant" -o "$type" = "" + then + continue + fi + + BOARDNO=`expr "$number" : '\([0-9]\):'` + PORTS=`expr "$alltherest" : '.*ports=\([0-9]*\)' | tr ',' ' '` + MINORS=`expr "$alltherest" : '.*minors=\([0-9,]*\)' | tr ',' ' '` + + if test "$BOARDNO" = "" -o "$PORTS" = "" + then +# This may be a bug. We should at least get this much information + echo "Unable to process board line" + continue + fi + + if test "$MINORS" = "" + then +# Silently skip this one. This board seems to have no boxes + continue + fi + + echo "board $BOARDNO: $type ports = $PORTS; port numbers = $MINORS" + + if test "$BRDMAJOR" != "" + then + BRDMINOR=`expr $BOARDNO \* 4` + STSMINOR=`expr $BRDMINOR + 1` + if test ! -c /dev/ip2ipl$BOARDNO ; then + mknod /dev/ip2ipl$BOARDNO c $BRDMAJOR $BRDMINOR + fi + if test ! -c /dev/ip2stat$BOARDNO ; then + mknod /dev/ip2stat$BOARDNO c $BRDMAJOR $STSMINOR + fi + fi + + if test "$TTYMAJOR" != "" + then + PORTNO=$BOARDBASE + + for PORTNO in $MINORS + do + if test ! -c /dev/ttyF$PORTNO ; then + # We got the harware but no device - make it + mknod /dev/ttyF$PORTNO c $TTYMAJOR $PORTNO + fi + done + fi + + if test "$CUAMAJOR" != "" + then + PORTNO=$BOARDBASE + + for PORTNO in $MINORS + do + if test ! -c /dev/cuf$PORTNO ; then + # We got the harware but no device - make it + mknod /dev/cuf$PORTNO c $CUAMAJOR $PORTNO + fi + done + fi +done + +exit 0 +===== Cut Here ===== diff -u --recursive --new-file v2.2.11/linux/Documentation/isdn/HiSax.cert linux/Documentation/isdn/HiSax.cert --- v2.2.11/linux/Documentation/isdn/HiSax.cert Mon Aug 9 16:05:54 1999 +++ linux/Documentation/isdn/HiSax.cert Wed Aug 25 17:29:45 1999 @@ -14,7 +14,8 @@ However, if you wish to modify the HiSax sources, please note the following: -HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards. +HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards +and Eicon Technology Diva 2.01 PCI card. The certification is only valid for the combination of the tested software version and the tested hardware. Any changes to the HiSax source code may therefore affect the certification. @@ -48,13 +49,14 @@ drivers/isdn/hisax/l3_1tr6.c drivers/isdn/hisax/cert.c drivers/isdn/hisax/elsa.c +drivers/isdn/hisax/diva.c Please send any changes, bugfixes and patches to me rather than implementing them directly into the HiSax sources. This does not reduce your rights granted by the GNU General Public License. If you wish to change the sources, go ahead; but note that then the -certification is invalid even if you use ELSA Quickstep cards. +certification is invalid even if you use one of the approved cards. Here are the certification registration numbers for ELSA Quickstep cards: German D133361J CETECOM ICT Services GmbH 0682 @@ -68,9 +70,9 @@ Version: 2.6.3i Charset: noconv -iQCVAwUBNj5OKDpxHvX/mS9tAQFHuQP/WeImlqCcDZ2d132yAvRBWFULlJoSf1P/ -c1lVTeaWvsSaY5Cu9hrKhXXhPzeEaitUbcUBPXdpzFWCA5CE902lnz7AhgRC+HF1 -0qiKgkZZyc/5HKasFymR1+IWSLw30GesP3Di/ZMR3NJi8SlY9PIjx7hnEMunGSRO -1ufPvfWWuu8= -=nGJk +iQCVAwUBN6xoKTpxHvX/mS9tAQF4DAP/efRWym6jvNOND1O9eaEFdP5fd2xKB3XD +Ifh6Iv0DvARcIuxXtEjT+z3FjjQk35eo/wX4C4tpRhYQYdgCxl+iv+5DzhVDpB95 +3QS9E5m0E1eIK3t8XiQTRgb+1JPCMYUThCrakYsX25o3ndGKyDipsCTfkyR38XwC +bUyTfcOYKAk= +=VKyL -----END PGP SIGNATURE----- diff -u --recursive --new-file v2.2.11/linux/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE --- v2.2.11/linux/Documentation/isdn/INTERFACE Mon Aug 9 16:05:54 1999 +++ linux/Documentation/isdn/INTERFACE Wed Aug 25 17:29:45 1999 @@ -1,4 +1,4 @@ -$Id: INTERFACE,v 1.12 1999/07/13 20:59:59 werner Exp $ +$Id: INTERFACE,v 1.13 1999/08/11 20:30:26 armin Exp $ Description of the Interface between Linklevel and Hardwarelevel of isdn4linux: @@ -216,7 +216,7 @@ Until now, the following commands are defined: -***CHANGEI1.34: The parameter "num" has been replaced by a union "para" containing +***CHANGEI1.34: The parameter "num" has been replaced by a union "parm" containing the old "num" and a new setup_type struct used for ISDN_CMD_DIAL and ISDN_STAT_ICALL callback. @@ -235,7 +235,7 @@ driver = driver-Id. command = ISDN_CMD_IOCTL arg = Original ioctl-cmd - IIOCDRVCTL - para.num = first bytes filled with (unsigned long)arg + parm.num = first bytes filled with (unsigned long)arg Returnvalue: Depending on driver. @@ -251,10 +251,10 @@ command = ISDN_CMD_DIAL arg = channel-number locally to the driver. (starting with 0) - para.setup.phone = An ASCII-String containing the number to dial. - para.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN. - para.setup.si1 = The Service-Indicator. - para.setup.si2 = Additional Service-Indicator. + parm.setup.phone = An ASCII-String containing the number to dial. + parm.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN. + parm.setup.si1 = The Service-Indicator. + parm.setup.si2 = Additional Service-Indicator. If the Line has been designed as SPV (a special german feature, meaning semi-leased-line) the phone has to @@ -272,7 +272,7 @@ driver = driver-Id. command = ISDN_CMD_ACCEPTD arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_CMD_ACCEPTB: @@ -283,7 +283,7 @@ driver = driver-Id. command = ISDN_CMD_ACCEPTB arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_CMD_HANGUP: @@ -295,7 +295,7 @@ driver = driver-Id. command = ISDN_CMD_HANGUP arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_CMD_CLREAZ: @@ -306,7 +306,7 @@ driver = driver-Id. command = ISDN_CMD_CLREAZ arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_CMD_SETEAZ: @@ -317,7 +317,7 @@ driver = driver-Id. command = ISDN_CMD_SETEAZ arg = channel-number locally to the driver. (starting with 0) - para.num = ASCII-String, containing the desired EAZ's/MSN's + parm.num = ASCII-String, containing the desired EAZ's/MSN's (comma-separated). If an empty String is given, the HL-driver should respond to ALL incoming calls, regardless of the destination-address. @@ -332,7 +332,7 @@ driver = driver-Id. command = ISDN_CMD_GETEAZ arg = channel-number locally to the driver. (starting with 0) - para.num = ASCII-String, containing the current EAZ's/MSN's + parm.num = ASCII-String, containing the current EAZ's/MSN's ISDN_CMD_SETSIL: (currently unused) @@ -343,7 +343,7 @@ driver = driver-Id. command = ISDN_CMD_SETSIL arg = channel-number locally to the driver. (starting with 0) - para.num = ASCII-String, containing the desired Service-Indicators. + parm.num = ASCII-String, containing the desired Service-Indicators. ISDN_CMD_GETSIL: (currently unused) @@ -354,7 +354,7 @@ driver = driver-Id. command = ISDN_CMD_SETSIL arg = channel-number locally to the driver. (starting with 0) - para.num = ASCII-String, containing the current Service-Indicators. + parm.num = ASCII-String, containing the current Service-Indicators. ISDN_CMD_SETL2: @@ -369,7 +369,7 @@ arg = channel-number locally to the driver. (starting with 0) logical or'ed with (protocol-Id << 8) protocol-Id is one of the constants ISDN_PROTO_L2... - para = unused. + parm = unused. ISDN_CMD_GETL2: (currently unused) @@ -380,7 +380,7 @@ driver = driver-Id. command = ISDN_CMD_GETL2 arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. Returnvalue: current protocol-Id (one of the constants ISDN_L2_PROTO) @@ -397,7 +397,7 @@ arg = channel-number locally to the driver. (starting with 0) logical or'ed with (protocol-Id << 8) protocol-Id is one of the constants ISDN_PROTO_L3... - para = unused. + parm.fax = Pointer to T30_s fax struct. (fax usage only) ISDN_CMD_GETL2: (currently unused) @@ -408,7 +408,7 @@ driver = driver-Id. command = ISDN_CMD_GETL3 arg = channel-number locally to the driver. (starting with 0) - para = unused. + parm = unused. Returnvalue: current protocol-Id (one of the constants ISDN_L3_PROTO) @@ -422,7 +422,7 @@ driver = driver-Id. command = ISDN_CMD_LOCK arg = unused. - para = unused. + parm = unused. ISDN_CMD_UNLOCK: @@ -434,7 +434,19 @@ driver = driver-Id. command = ISDN_CMD_UNLOCK arg = unused. - para = unused. + parm = unused. + + ISDN_CMD_FAXCMD: + + With this command the HL-driver receives a fax sub-command. + For details refer to INTERFACE.fax + + Parameter: + driver = driver-Id. + command = ISDN_CMD_FAXCMD + arg = channel-number locally to the driver. (starting with 0) + parm = unused. + 3. Description of the events to be signaled by the HL-driver to the LL. @@ -456,7 +468,7 @@ driver = driver-Id command = ISDN_STAT_STAVAIL arg = length of available data. - para = unused. + parm = unused. ISDN_STAT_ICALL: @@ -466,12 +478,12 @@ driver = driver-Id command = ISDN_STAT_ICALL arg = channel-number, locally to the driver. (starting with 0) - para.setup.phone = Callernumber. - para.setup.eazmsn = CalledNumber. - para.setup.si1 = Service Indicator. - para.setup.si2 = Additional Service Indicator. - para.setup.plan = octet 3 from Calling party number Information Element. - para.setup.screen = octet 3a from Calling party number Information Element. + parm.setup.phone = Callernumber. + parm.setup.eazmsn = CalledNumber. + parm.setup.si1 = Service Indicator. + parm.setup.si2 = Additional Service Indicator. + parm.setup.plan = octet 3 from Calling party number Information Element. + parm.setup.screen = octet 3a from Calling party number Information Element. Return: 0 = No device matching this call. @@ -497,7 +509,7 @@ driver = driver-Id command = ISDN_STAT_RUN arg = unused. - para = unused. + parm = unused. ISDN_STAT_STOP: @@ -508,7 +520,7 @@ driver = driver-Id command = ISDN_STAT_STOP arg = unused. - para = unused. + parm = unused. ISDN_STAT_DCONN: @@ -519,7 +531,7 @@ driver = driver-Id command = ISDN_STAT_DCONN arg = channel-number, locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_STAT_BCONN: @@ -534,7 +546,7 @@ driver = driver-Id command = ISDN_STAT_BCONN arg = channel-number, locally to the driver. (starting with 0) - para.num = ASCII-String, containing type of connection (for analog + parm.num = ASCII-String, containing type of connection (for analog modem only). This will be appended to the CONNECT message e.g. 14400/V.32bis @@ -549,7 +561,7 @@ driver = driver-Id command = ISDN_STAT_DHUP arg = channel-number, locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_STAT_BHUP: @@ -564,7 +576,7 @@ driver = driver-Id command = ISDN_STAT_BHUP arg = channel-number, locally to the driver. (starting with 0) - para = unused. + parm = unused. ISDN_STAT_CINF: @@ -575,7 +587,7 @@ driver = driver-Id command = ISDN_STAT_CINF arg = channel-number, locally to the driver. (starting with 0) - para.num = ASCII string containing charge-units (digits only). + parm.num = ASCII string containing charge-units (digits only). ISDN_STAT_LOAD: (currently unused) @@ -588,7 +600,7 @@ driver = driver-Id command = ISDN_STAT_UNLOAD arg = unused. - para = unused. + parm = unused. ISDN_STAT_BSENT: @@ -600,7 +612,7 @@ driver = driver-Id command = ISDN_STAT_BSENT arg = channel-number, locally to the driver. (starting with 0) - para.length = ***CHANGEI.1.21: New field. + parm.length = ***CHANGEI.1.21: New field. the driver has to set this to the original length of the skb at the time of receiving it from the linklevel. @@ -613,21 +625,21 @@ driver = driver-Id command = ISDN_STAT_NODCH arg = channel-number, locally to the driver. (starting with 0) - para = unused. + parm = unused. - ISDN_STAT_ADDCH: (currently unused) + ISDN_STAT_ADDCH: - This call is planned for HL-drivers, which are unable to check card-type + This call is for HL-drivers, which are unable to check card-type or numbers of supported channels before they have loaded any firmware - using ioctl. Those HL-driver simply set the channel-parameter to zero - or a minimum channel-number when registering, and later if they know + using ioctl. Those HL-driver simply set the channel-parameter to a + minimum channel-number when registering, and later if they know the real amount, perform this call, allocating additional channels. Parameter: driver = driver-Id command = ISDN_STAT_ADDCH - arg = to be defined. - para = to be defined. + arg = number of channels to be added. + parm = unused. ISDN_STAT_CAUSE: @@ -640,7 +652,7 @@ driver = driver-Id command = ISDN_STAT_NODCH arg = channel-number, locally to the driver. (starting with 0) - para.num = ASCII string containing CAUSE-message. + parm.num = ASCII string containing CAUSE-message. ISDN_STAT_L1ERR: @@ -653,7 +665,7 @@ driver = driver-Id command = ISDN_STAT_L1ERR arg = channel-number, locally to the driver. (starting with 0) - para.errcode= ISDN_STAT_L1ERR_SEND: Packet lost while sending. + parm.errcode= ISDN_STAT_L1ERR_SEND: Packet lost while sending. ISDN_STAT_L1ERR_RECV: Packet lost while receiving. ISDN_STAT_DISCH: @@ -672,4 +684,14 @@ arg = channel-number, locally to the driver. (starting with 0) parm.num[0] = 0 if channel shall be disabled, else enabled. + ISDN_STAT_FAXIND: + + With this call the HL-driver signals a fax sub-command to the LL. + For details refer to INTERFACE.fax + + Parameter: + driver = driver-Id. + command = ISDN_STAT_FAXIND + arg = channel-number, locally to the driver. (starting with 0) + parm = unused. diff -u --recursive --new-file v2.2.11/linux/Documentation/isdn/INTERFACE.fax linux/Documentation/isdn/INTERFACE.fax --- v2.2.11/linux/Documentation/isdn/INTERFACE.fax Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/INTERFACE.fax Wed Aug 25 17:29:45 1999 @@ -0,0 +1,163 @@ +$Id: INTERFACE.fax,v 1.1 1999/08/11 20:30:28 armin Exp $ + + +Description of the fax-subinterface between linklevel and hardwarelevel of + isdn4linux. + + The communication between linklevel (LL) and harwarelevel (HL) for fax + is based on the struct T30_s (defined in isdnif.h). + This struct is allocated in the LL. + In order to use fax, the LL provides the pointer to this struct with the + command ISDN_CMD_SETL3 (parm.fax). This pointer expires in case of hangup + and when a new channel to a new connection is assigned. + + +Data handling: + In send-mode the HL-driver has to handle the codes and the bit-order + conversion by itself. + In receive-mode the LL-driver takes care of the bit-order conversion + (specified by +FBOR) + +Structure T30_s description: + + This structure stores the values (set by AT-commands), the remote- + capability-values and the command-codes between LL and HL. + + If the HL-driver receives ISDN_CMD_FAXCMD, all needed information + is in this struct set by the LL. + To signal information to the LL, the HL-driver has to set the + the parameters and use ISDN_STAT_FAXIND. + (Please refer to INTERFACE) + +Structure T30_s: + + All members are 8-bit unsigned (__u8) + + - resolution + - rate + - width + - length + - compression + - ecm + - binary + - scantime + - id[] + Local faxmachine's parameters, set by +FDIS, +FDCS, +FLID, ... + + - r_resolution + - r_rate + - r_width + - r_length + - r_compression + - r_ecm + - r_binary + - r_scantime + - r_id[] + Remote faxmachine's parameters. To be set by HL-driver. + + - phase + Defines the actual state of fax connection. Set by HL or LL + depending on progress and type of connection. + If the phase changes because of an AT command, the LL driver + changes this value. Otherwise the HL-driver takes care of it, but + only neccessary on call establishment (from IDLE to PHASE_A). + (one of the constants ISDN_FAX_PHASE_[IDLE,A,B,C,D,E]) + + - direction + Defines outgoing/send or incoming/receive connection. + (ISDN_TTY_FAX_CONN_[IN,OUT]) + + - code + Commands from LL to HL; possible constants : + ISDN_TTY_FAX_DR signals +FDR command to HL + + ISDN_TTY_FAX_DT signals +FDT command to HL + + ISDN_TTY_FAX_ET signals +FET command to HL + + + Other than that the "code" is set with the hangup-code value at + the end of connection for the +FHNG message. + + - r_code + Commands from HL to LL; possible constants : + ISDN_TTY_FAX_CFR output of +FCFR message. + + ISDN_TTY_FAX_RID output of remote ID set in r_id[] + (+FCSI/+FTSI on send/receive) + + ISDN_TTY_FAX_DCS output of +FDCS and CONNECT message, + switching to phase C. + + ISDN_TTY_FAX_ET signals end of data, + switching to phase D. + + ISDN_TTY_FAX_FCON signals the established, outgoing connection, + switching to phase B. + + ISDN_TTY_FAX_FCON_I signals the established, incoming connection, + switching to phase B. + + ISDN_TTY_FAX_DIS output of +FDIS message and values. + + ISDN_TTY_FAX_SENT signals that all data has been sent + and is acknowledged, + OK message will be sent. + + ISDN_TTY_FAX_PTS signals a msg-confirmation (page sent successful), + depending on fet value: + 0: output OK message (more pages follow) + 1: switching to phase B (next document) + + ISDN_TTY_FAX_TRAIN_OK output of +FDCS and OK message (for receive mode). + + ISDN_TTY_FAX_EOP signals end of data in receive mode, + switching to phase D. + + ISDN_TTY_FAX_HNG output of the +FHNG and value set by code and + OK message, switching to phase E. + + + - badlin + Value of +FBADLIN + + - badmul + Value of +FBADMUL + + - bor + Value of +FBOR + + - fet + Value of +FET command in send-mode. + Set by HL in receive-mode for +FET message. + + - pollid[] + ID-string, set by +FCIG + + - cq + Value of +FCQ + + - cr + Value of +FCR + + - ctcrty + Value of +FCTCRTY + + - minsp + Value of +FMINSP + + - phcto + Value of +FPHCTO + + - rel + Value of +FREL + + - nbc + Value of +FNBC (0,1) + (+FNBC is not a known class 2 fax command, I added this to change the + automatic "best capabilities" connection in the eicon HL-driver) + + +Armin +mac@melware.de + diff -u --recursive --new-file v2.2.11/linux/Documentation/isdn/README linux/Documentation/isdn/README --- v2.2.11/linux/Documentation/isdn/README Mon Aug 9 16:05:54 1999 +++ linux/Documentation/isdn/README Wed Aug 25 17:29:45 1999 @@ -149,6 +149,8 @@ AT&X0 BTX-mode and T.70-mode off (default) AT&X1 BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0) AT&X2 T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0) + AT+Rx Resume a suspended call with CallID x (x = 1,2,3...) + AT+Sx Suspend a call with CallID x (x = 1,2,3...) For voice-mode commands refer to README.audio diff -u --recursive --new-file v2.2.11/linux/Documentation/isdn/README.diversion linux/Documentation/isdn/README.diversion --- v2.2.11/linux/Documentation/isdn/README.diversion Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/README.diversion Wed Aug 25 17:29:45 1999 @@ -0,0 +1,127 @@ +The isdn diversion services are a supporting module working together with +the isdn4linux and the HiSax module for passive cards. +Active cards, TAs and cards using a own or other driver than the HiSax +module need to be adapted to the HL<->LL interface described in a separate +document. The diversion services may be used with all cards supported by +the HiSax driver. +The diversion kernel interface and controlling tool divertctrl were written +by Werner Cornelius (werner@isdn4linux.de or werner@titro.de) under the +GNU Public 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 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. + +Table of contents +================= + +1. Features of the i4l diversion services + (Or what can the i4l diversion services do for me) + +2. Required hard- and software + +3. Compiling, installing and loading/unloading the module + Tracing calling and diversion information + +4. Tracing calling and diversion information + +5. Format of the divert device ASCII output + + +1. Features of the i4l diversion services + (Or what can the i4l diversion services do for me) + + The i4l diversion services offers call forwarding and logging normally + only supported by isdn phones. Incoming calls may be diverted + unconditionally (CFU), when not reachable (CFNR) or on busy condition + (CFB). + The diversions may be invoked statically in the providers exchange + as normally done by isdn phones. In this case all incoming calls + with a special (or all) service identifiers are forwarded if the + forwarding reason is met. Activated static services may also be + interrogated (queried). + The i4l diversion services additionally offers a dynamic version of + call forwarding which is not preprogrammed inside the providers exchange + but dynamically activated by i4l. + In this case all incoming calls are checked by rules that may be + compared to the mechanism of ipfwadm or ipchains. If a given rule matches + the checking process is finished and the rule matching will be applied + to the call. + The rules include primary and secondary service indentifiers, called + number and subaddress, callers number and subaddress and whether the rule + matches to all filtered calls or only those when all B-channel resources + are exhausted. + Actions that may be invoked by a rule are ignore, proceed, reject, + direct divert or delayed divert of a call. + All incoming calls matching a rule except the ignore rule a reported and + logged as ASCII via the proc filesystem (/proc/net/isdn/divert). If proceed + is selected the call will be held in a proceeding state (without ringing) + for a certain amount of time to let an external program or client decide + how to handle the call. + + +2. Required hard- and software + + For using the i4l diversion services the isdn line must be of a EURO/DSS1 + type. Additionally the i4l services only work together with the HiSax + driver for passive isdn cards. All HiSax supported cards may be used for + the diversion purposes. + The static diversion services require the provider having static services + CFU, CFNR, CFB activated on an MSN-line. The static services may not be + used on a point-to-point connection. Further the static services are only + available in some countries (for example germany). Countries requiring the + keypad protocol for activating static diversions (like the netherlands) are + not supported but may use the tty devices for this purpose. + The dynamic diversion servives may be used in all countries if the provider + enables the feature CF (call forwarding). This should work on both MSN- and + point-to-point lines. + To add and delete rules the additional divertctrl program is needed. This + program is part of the isdn4kutils package. + +3. Compiling, installing and loading/unloading the module + Tracing calling and diversion information + + + To compile the i4l code with diversion support you need to say yes to the + DSS1 diversion services when selecting the i4l options in the kernel + config (menuconfig or config). + After having properly activated a make modules and make modules_install all + required modules will be correctly installed in the needed modules dirs. + As the diversion services are currently not included in the scripts of most + standard distributions you will have to add a "insmod dss1_divert" after + having loaded the global isdn module. + The module can be loaded without any command line parameters. + If the module is actually loaded and active may be checked with a + "cat /proc/modules" or "ls /proc/net/isdn/divert". The divert file is + dynamically created by the diversion module and removed when the module is + unloaded. + + +4. Tracing calling and diversion information + + You also may put a "cat /proc/net/isdn/divert" in the background with the + output redirected to a file. Then all actions of the module are logged. + The divert file in the proc system may be opened more than once, so in + conjunction with inetd and a small remote client on other machines inside + your network incoming calls and reactions by the module may be shown on + every listening machine. + If a call is reported as proceeding an external program or client may + specify during a certain amount of time (normally 4 to 10 seconds) what + to do with that call. + To unload the module all open files to the device in the proc system must + be closed. Otherwise the module (and isdn.o) may not be unloaded. + +5. Format of the divert device ASCII output + + To be done later + diff -u --recursive --new-file v2.2.11/linux/Documentation/isdn/README.fax linux/Documentation/isdn/README.fax --- v2.2.11/linux/Documentation/isdn/README.fax Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/README.fax Wed Aug 25 17:29:45 1999 @@ -0,0 +1,38 @@ + +Fax with isdn4linux +=================== + +When enabled during kernel configuration, the tty emulator +of the ISDN subsystem is capable of the Fax Class 2 commands. + +This only makes sense under the following conditions : + +- You need the commands as dummy, because you are using + hylafax (with patch) for AVM capi. +- You want to use the fax capabillities of your isdn-card. + (supported cards are listed below) + + +NOTE: This implementation does *not* support fax with passive + ISDN-cards (known as softfax). The low-level driver of + the ISDN-card and/or the card itself must support this. + + +Supported ISDN-Cards +-------------------- + +Eicon DIVA Server BRI/PCI (will be ready soon) +Eicon DIVA Server PRI/PCI (will be ready soon) + + + +The command set is known as Class 2 (not Class 2.0) and +can be activated by AT+FCLASS=2 + + +The interface between the link-level-module and the hardware-level driver +is described in the files INTERFACE.fax and INTERFACE. + +Armin +mac@melware.de + diff -u --recursive --new-file v2.2.11/linux/Documentation/isdn/README.hfc-pci linux/Documentation/isdn/README.hfc-pci --- v2.2.11/linux/Documentation/isdn/README.hfc-pci Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/README.hfc-pci Wed Aug 25 17:29:45 1999 @@ -0,0 +1,26 @@ +The driver for the HFC-PCI and HFC-PCI-A chips from CCD may be used +for many OEM cards using this chips. +Additionally the driver has a special feature which makes it possible +to read the echo-channel of the isdn bus. So all frames in both directions +may be logged. +When the echo logging feature is used the number of available B-channels +for a HFC-PCI card is reduced to 1. Of course this is only relevant to +the card, not to the isdn line. +To activate the echo mode the following ioctls must be entered: + +hisaxctrl 10 1 + +This reduces the available channels to 1. There must not be open connections +through this card when entering the command. +And then: + +hisaxctrl 12 1 + +This enables the echo mode. If Hex logging is activated the isdnctrlx +devices show a output with a line beginning of HEX: for the providers +exchange and ECHO: for isdn devices sending to the provider. + +Comments and reports to werner@isdn4linux.de or werner@titro.de . + + + diff -u --recursive --new-file v2.2.11/linux/Documentation/locks.txt linux/Documentation/locks.txt --- v2.2.11/linux/Documentation/locks.txt Thu Jan 7 08:41:54 1999 +++ linux/Documentation/locks.txt Wed Aug 25 17:29:45 1999 @@ -13,12 +13,11 @@ The old flock(2) emulation in the kernel was swapped for proper BSD compatible flock(2) support in the 1.3.x series of kernels. With the -release of the 2.1.x kernel series, support for the old emulation has -been totally removed, so that we don't need to carry this baggage -forever. +release of the 2.1.x kernel series, support for the old emulation was +totally removed, so that we don't need to carry this baggage forever. -This should not cause problems for anybody, since everybody using a -2.1.x kernel should have updated their C library to a suitable version +This should not cause problems for anybody, since everybody using a 2.1.x +or 2.2.x kernel should have updated their C library to a suitable version anyway (see the file "linux/Documentation/Changes".) 1.2 Allow Mixed Locks Again @@ -31,9 +30,9 @@ for example. This gave rise to some other subtle problems if sendmail was configured to rebuild the alias file. Sendmail tried to lock the aliases.dir file with fcntl() at the same time as the GDBM routines tried to lock this -file with flock(). With pre 1.3.96 kernels this could result in deadlocks that, -over time, or under a very heavy mail load, would eventually cause the kernel -to lock solid with deadlocked processes. +file with flock(). With kernels before 1.3.96 this could result in deadlocks +that, over time or under a very heavy mail load, would eventually cause the +kernel to lock solid with deadlocked processes. 1.2.2 The Solution @@ -60,7 +59,7 @@ file for which a mandatory lock existed. From this release of the kernel, mandatory locking can be turned on and off -on a per-filesystem basis, using the mount options 'mand' and 'nomand'. +on a per-file-system basis, using the mount options 'mand' and 'nomand'. The default is to disallow mandatory locking. The intention is that mandatory locking only be enabled on a local filesystem as the specific need arises. diff -u --recursive --new-file v2.2.11/linux/Documentation/m68k/kernel-options.txt linux/Documentation/m68k/kernel-options.txt --- v2.2.11/linux/Documentation/m68k/kernel-options.txt Tue May 11 09:57:14 1999 +++ linux/Documentation/m68k/kernel-options.txt Wed Aug 25 17:29:45 1999 @@ -1,5 +1,3 @@ - - Command Line Options for Linux/m68k =================================== @@ -14,7 +12,7 @@ Often I've been asked which command line options the Linux/m68k kernel understands, or how the exact syntax for the ... option is, or ... about the option ... . I hope, this document supplies all the -answers... +answers. Note that some options might be outdated, their descriptions being incomplete or missing. Please update the information and send in the @@ -110,20 +108,19 @@ [Strange and maybe uninteresting stuff ON] This unusual translation of device names has some strange -consequences: If, for example, you have a symbolic link from /dev/fd +consequences: if, for example, you have a symbolic link from /dev/fd to /dev/fd0D720 as an abbreviation for floppy driver #0 in DD format, you cannot use this name for specifying the root device, because the kernel cannot see this symlink before mounting the root FS and it isn't in the table above. If you use it, the root device will not be -set at all, without an error message. Another example: You cannot use a +set at all, without an error message. Another example: you cannot use a partition on e.g. the sixth SCSI disk as the root filesystem, if you want to specify it by name. This is, because only the devices up to -/dev/sde are in the table above, but not /dev/sdf. Although, you can -use the sixth SCSI disk for the root FS, but you have to specify the -device by number... (see below). Or, even more strange, you can use the -fact that there is no range checking of the partition number, and your -knowledge that each disk uses 16 minors, and write "root=/dev/sde17" -(for /dev/sdf1). +/dev/sde are in the table above, but not /dev/sdf. You can use the sixth +SCSI disk for the root filesystem, but you have to specify the device by +number (see below). Even more strange, you can use the fact that there is +no range checking of the partition number, and your knowledge that each +disk uses 16 minors, and write "root=/dev/sde17" (for /dev/sdf1). [Strange and maybe uninteresting stuff OFF] @@ -181,7 +178,7 @@ Devices possible for Amiga: - "ser": built-in serial port; parameters: 9600bps, 8N1 - - "mem": Save the messages to a reserved area in chip mem. After + - "mem": Save the messages to a reserved area in chip memory. After rebooting, they can be read under AmigaOS with the tool 'dmesg'. @@ -205,7 +202,7 @@ Syntax: ramdisk= This option instructs the kernel to set up a ramdisk of the given -size in KBytes. Do not use this option if the ramdisk contents are +size in kilobytes. Do not use this option if the ramdisk contents are passed by bootstrap! In this case, the size is selected automatically and should not be overwritten. @@ -234,10 +231,10 @@ drivers/net/Space.c in the Linux source. Most prominent are eth0, ... eth3, sl0, ... sl3, ppp0, ..., ppp3, dummy, and lo. - The non-ethernet drivers (sl, ppp, dummy, lo) obviously ignore the -settings by this options. Also, the existing ethernet drivers for + The non-Ethernet drivers (sl, ppp, dummy, lo) obviously ignore the +settings by this options. Also, the existing Ethernet drivers for Linux/m68k (ariadne, a2065, hydra) don't use them because Zorro boards -are really Plug-'n-Play, so the "ether=" option is useless altogether +are really Plug-'n'-Play, so the "ether=" option is useless altogether for Linux/m68k. @@ -288,8 +285,8 @@ to use (minimum 4, default 4), is the size of each buffer in kilobytes (minimum 4, default 32) and says how much percent of error will be tolerated when setting a frequency -(maximum 10, default 0). For example with 3% you can play 8000Hz -AU-Files on the Falcon with its hardware frequency of 8195Hz and thus +(maximum 10, default 0). For example, with 3% you can play 8000 Hz Sun +audio files on the Falcon with its hardware frequency of 8195 Hz and thus don't need to expand the sound. @@ -482,7 +479,7 @@ 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, +therefore we don't support hardware-dependent functions like hardware scroll, panning or blanking. 4.1.7) eclock: @@ -653,7 +650,7 @@ "ov_midi", ... These options are meant for switching on an OverScan video extension. The difference to the bare option is that the switch-on is done after video initialization, and somehow synchronized -to the HBLANK. A speciality is that ov_ikbd and ov_midi are switched +to the HBLANK. A specialty is that ov_ikbd and ov_midi are switched off before rebooting, so that OverScan is disabled and TOS boots correctly. @@ -737,8 +734,8 @@ - vga70 : 640x400, 31 kHz, 70 Hz Please notice that the ECS and VGA modes require either an ECS or AGA -chipset, and that these modes are limited to 2-bit color for the ECS -chipset and 8-bit color for the AGA chipset. +chip set, and that these modes are limited to 2-bit color for the ECS +chip set and 8-bit color for the AGA chip set. 5.1.2) depth ------------ @@ -842,11 +839,11 @@ Syntax: clock:x x = clock input in MHz for WD33c93 chip. Normal values would be from -8 through 20. The default value depends on your hostadapter(s), +8 through 20. The default value depends on your host adapter(s), default for the A3000 internal controller is 14, for the A2091 it's 8 -and for the GVP hostadapters it's either 8 or 14, depending on the -hostadapter and the SCSI-clock jumper present on some GVP -hostadapters. +and for the GVP host adapters it's either 8 or 14, depending on the +host adapter and the SCSI-clock jumper present on some GVP +host adapters. 5.3.6) next ----------- @@ -875,8 +872,8 @@ The earlier versions of the GVP driver did not handle DMA address-mask settings correctly which made it necessary for some people to use this option, in order to get their GVP controller -running under Linux. These problems have hopefully been solved and the -use of this option is now highly unrecommended! +running under Linux. These problems should now be solved and +further use of this option is highly discouraged! Incorrect use can lead to unpredictable behavior, so please only use this option if you *know* what you are doing and have a reason to do diff -u --recursive --new-file v2.2.11/linux/Documentation/modules.txt linux/Documentation/modules.txt --- v2.2.11/linux/Documentation/modules.txt Tue Mar 10 14:43:13 1998 +++ linux/Documentation/modules.txt Wed Aug 25 17:29:45 1999 @@ -59,7 +59,7 @@ Most low-level SCSI drivers: (i.e. aha1542, in2000) All SCSI high-level drivers: disk, tape, cdrom, generic. - Most ethernet drivers: (too many to list, please see the file + Most Ethernet drivers: (too many to list, please see the file ./Documentation/networking/net-modules.txt) Most CDROM drivers: diff -u --recursive --new-file v2.2.11/linux/Documentation/mtrr.txt linux/Documentation/mtrr.txt --- v2.2.11/linux/Documentation/mtrr.txt Mon May 10 10:32:45 1999 +++ linux/Documentation/mtrr.txt Wed Aug 25 17:29:45 1999 @@ -40,7 +40,7 @@ reg01: base=0x08000000 ( 128MB), size= 64MB: write-back, count=1 reg02: base=0xf8000000 (3968MB), size= 4MB: write-combining, count=1 -This is for videoram at base address 0xf8000000 and size 4 MBytes. To +This is for video RAM at base address 0xf8000000 and size 4 megabytes. To find out your base address, you need to look at the output of your X server, which tells you where the linear framebuffer address is. A typical line that you may get is: @@ -56,7 +56,7 @@ (--) S3: videoram: 4096k -That's 4 MBytes, which is 0x400000 bytes (in hexadecimal). +That's 4 megabytes, which is 0x400000 bytes (in hexadecimal). A patch is being written for XFree86 which will make this automatic: in other words the X server will manipulate /proc/mtrr using the ioctl() interface, so users won't have to do anything. If you use a diff -u --recursive --new-file v2.2.11/linux/Documentation/networking/00-INDEX linux/Documentation/networking/00-INDEX --- v2.2.11/linux/Documentation/networking/00-INDEX Wed May 20 18:54:34 1998 +++ linux/Documentation/networking/00-INDEX Wed Aug 25 17:29:45 1999 @@ -13,9 +13,9 @@ alias.txt - info on using alias network devices arcnet-hardware.txt - - tons of info on arcnet, hubs, arcnet card jumper settings, etc. + - tons of info on ARCnet, hubs, jumper settings for ARCnet cards, etc. arcnet.txt - - info on the using the arcnet driver itself. + - info on the using the ARCnet driver itself. ax25.txt - info on using AX.25 and NET/ROM code for Linux baycom.txt @@ -69,13 +69,13 @@ smc9.txt - the driver for SMC's 9000 series of Ethernet cards soundmodem.txt - - Linux driver for soundcards as AX.25 modems + - Linux driver for sound cards as AX.25 modems tcp.txt - short blurb on how TCP output takes place. tulip.txt - info on using DEC 21040/21041/21140 based PCI Ethernet cards. vortex.txt - - info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) e'net cards. + - info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) Ethernet cards. wan-router.txt - Wan router documentation wanpipe.txt diff -u --recursive --new-file v2.2.11/linux/Documentation/networking/baycom.txt linux/Documentation/networking/baycom.txt --- v2.2.11/linux/Documentation/networking/baycom.txt Sun Jun 7 11:13:44 1998 +++ linux/Documentation/networking/baycom.txt Wed Aug 25 17:29:45 1999 @@ -31,7 +31,7 @@ Its devices are called bcp0 through bcp3. baycom_epp: - This driver supports the epp modem. + This driver supports the EPP modem. Its devices are called bce0 through bce3. This driver is work-in-progress. @@ -60,10 +60,10 @@ an additional power supply. Furthermore, it incorporates a carrier detect circuitry. -epp: This is a high speed modem adaptor that connects to an enhanced parallel port. +EPP: This is a high-speed modem adaptor that connects to an enhanced parallel port. Its target audience is users working over a high speed hub (76.8kbit/s). -eppfpga: This is a redesign of the epp adaptor. +eppfpga: This is a redesign of the EPP adaptor. diff -u --recursive --new-file v2.2.11/linux/Documentation/networking/cs89x0.txt linux/Documentation/networking/cs89x0.txt --- v2.2.11/linux/Documentation/networking/cs89x0.txt Fri Sep 11 11:21:57 1998 +++ linux/Documentation/networking/cs89x0.txt Wed Aug 25 17:29:45 1999 @@ -203,7 +203,7 @@ * io=### - specify IO address (200h-360h) * irq=## - specify interrupt level * mmode=##### - specify memory base address -* dma=# - specify dma channel +* dma=# - specify DMA channel * media=rj45 - specify media type or media=2 or media=aui @@ -412,33 +412,33 @@ assigned during hardware configuration. The following tests are performed: * IO Register Read/Write Test - The IO Register Read/Write test insures that the CS8900/20 can be + The IO Register Read/Write test ensures that the CS8900/20 can be accessed in IO mode, and that the IO base address is correct. * Shared Memory Test - The Shared Memory test insures the CS8900/20 can be accessed in memory + The Shared Memory test ensures the CS8900/20 can be accessed in memory mode and that the range of memory addresses assigned does not conflict with other devices in the system. * Interrupt Test - The Interrupt test insures there are no conflicts with the assigned IRQ + The Interrupt test ensures there are no conflicts with the assigned IRQ signal. * EEPROM Test - The EEPROM test insures the EEPROM can be read. + The EEPROM test ensures the EEPROM can be read. * Chip RAM Test - The Chip RAM test insures the 4K of memory internal to the CS8900/20 is + The Chip RAM test ensures the 4 K of memory internal to the CS8900/20 is working properly. * Internal Loop-back Test - The Internal Loop Back test insures the adapter's transmitter and + The Internal Loop Back test ensures the adapter's transmitter and receiver are operating properly. If this test fails, make sure the adapter's cable is connected to the network (check for LED activity for example). * Boot PROM Test - The Boot PROM test insures the Boot PROM is present, and can be read. + The Boot PROM test ensures the Boot PROM is present, and can be read. Failure indicates the Boot PROM was not successfully read due to a hardware problem or due to a conflicts on the Boot PROM address assignment. (Test only applies if the adapter is configured to use the @@ -564,7 +564,7 @@ Telephone :(800) 888-5016 (from inside U.S. and Canada) :(512) 442-7555 (from outside the U.S. and Canada) Fax :(512) 912-3871 -Email :ethernet@crystal.cirrus.com +E-mail :ethernet@crystal.cirrus.com WWW :http://www.crystal.com diff -u --recursive --new-file v2.2.11/linux/Documentation/networking/multicast.txt linux/Documentation/networking/multicast.txt --- v2.2.11/linux/Documentation/networking/multicast.txt Thu Jun 4 22:53:50 1998 +++ linux/Documentation/networking/multicast.txt Wed Aug 25 17:29:45 1999 @@ -1,11 +1,13 @@ -Behaviour of cards under Multicast. This is how they currently -behave not what the hardware can do - i.e. the lance driver doesn't -use its filter, even though the code for loading it is in the DEC -lance based driver. +Behaviour of Cards Under Multicast +================================== -The following multicast requirements are needed +This is how they currently behave, not what the hardware can do--for example, +the Lance driver doesn't use its filter, even though the code for loading +it is in the DEC Lance-based driver. + +The following are requirements for multicasting ----------------------------------------------- -Appletalk Multicast hardware filtering not important but +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 diff -u --recursive --new-file v2.2.11/linux/Documentation/sound/MAD16 linux/Documentation/sound/MAD16 --- v2.2.11/linux/Documentation/sound/MAD16 Thu Apr 29 11:53:41 1999 +++ linux/Documentation/sound/MAD16 Wed Aug 25 17:29:45 1999 @@ -32,3 +32,22 @@ options mad16 io=0x530 irq=7 dma=0 dma16=1 mpu_io=816 mpu_irq=5 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 The addition of the "mpu_io=816 mpu_irq=5" to the mad16 options line is + +------------------------------------------------------------------------ +The mad16 module in addition supports the following options: + +option: meaning: default: +joystick=0,1 disabled, enabled disabled +cdtype=0x00,0x02,0x04, disabled, Sony CDU31A, disabled + 0x06,0x08,0x0a Mitsumi, Panasonic, + Secondary IDE, Primary IDE +cdport=0x340,0x320, 0x340 + 0x330,0x360 +cdirq=0,3,5,7,9,10,11 disabled, IRQ3, ... disabled +cddma=0,5,6,7 disabled, DMA5, ... DMA5 for Mitsumi or IDE +cddma=0,1,2,3 disabled, DMA1, ... DMA3 for Sony or Panasonic +opl4=0,1 OPL3, OPL4 OPL3 + +for more details see linux/drivers/sound/mad16.c + +Rui Sousa diff -u --recursive --new-file v2.2.11/linux/Documentation/sound/solo1 linux/Documentation/sound/solo1 --- v2.2.11/linux/Documentation/sound/solo1 Wed Dec 31 16:00:00 1969 +++ linux/Documentation/sound/solo1 Wed Aug 25 17:29:45 1999 @@ -0,0 +1,48 @@ +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (or later, available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +The card has an OPL compatible FM synthesizer. + +Thomas Sailer +sailer@ife.ee.ethz.ch diff -u --recursive --new-file v2.2.11/linux/Documentation/sound/vwsnd linux/Documentation/sound/vwsnd --- v2.2.11/linux/Documentation/sound/vwsnd Wed Dec 31 16:00:00 1969 +++ linux/Documentation/sound/vwsnd Wed Aug 25 17:29:45 1999 @@ -0,0 +1,293 @@ +vwsnd - Sound driver for the Silicon Graphics 320 and 540 Visual +Workstations' onboard audio. + +Copyright 1999 Silicon Graphics, Inc. All rights reserved. + + +At the time of this writing, March 1999, there are two models of +Visual Workstation, the 320 and the 540. This document only describes +those models. Future Visual Workstation models may have different +sound capabilities, and this driver will probably not work on those +boxes. + +The Visual Workstation has an Analog Devices AD1843 "SoundComm" audio +codec chip. The AD1843 is accessed through the Cobalt I/O ASIC, also +known as Lithium. This driver programs both both chips. + +============================================================================== +QUICK CONFIGURATION + + # insmod soundcore + # insmod vwsnd + +============================================================================== +I/O CONNECTIONS + +On the Visual Workstation, only three of the AD1843 inputs are hooked +up. The analog line in jacks are connected to the AD1843's AUX1 +input. The CD audio lines are connected to the AD1843's AUX2 input. +The microphone jack is connected to the AD1843's MIC input. The mic +jack is mono, but the signal is delivered to both the left and right +MIC inputs. You can record in stereo from the mic input, but you will +get the same signal on both channels (within the limits of A/D +accuracy). Full scale on the Line input is +/- 2.0 V. Full scale on +the MIC input is 20 dB less, or +/- 0.2 V. + +The AD1843's LOUT1 outputs are connected to the Line Out jacks. The +AD1843's HPOUT outputs are connected to the speaker/headphone jack. +LOUT2 is not connected. Line out's maximum level is +/- 2.0 V peak to +peak. The speaker/headphone out's maximum is +/- 4.0 V peak to peak. + +The AD1843's PCM input channel and one of its output channels (DAC1) +are connected to Lithium. The other output channel (DAC2) is not +connected. + +============================================================================== +CAPABILITIES + +The AD1843 has PCM input and output (Pulse Code Modulation, also known +as wavetable). PCM input and output can be mono or stereo in any of +four formats. The formats are 16 bit signed and 8 bit unsigned, +u-Law, and A-Law format. Any sample rate from 4 KHz to 49 KHz is +available, in 1 Hz increments. + +The AD1843 includes an analog mixer that can mix all three input +signals (line, mic and CD) into the analog outputs. The mixer has a +separate gain control and mute switch for each input. + +There are two outputs, line out and speaker/headphone out. They +always produce the same signal, and the speaker always has 3 dB more +gain than the line out. The speaker/headphone output can be muted, +but this driver does not export that function. + +The hardware can sync audio to the video clock, but this driver does +not have a way to specify syncing to video. + +============================================================================== +PROGRAMMING + +This section explains the API supported by the driver. Also see the +Open Sound Programming Guide at http://www.opensound.com/pguide/ . +This section assumes familiarity with that document. + +The driver has two interfaces, an I/O interface and a mixer interface. +There is no MIDI or sequencer capability. + +============================================================================== +PROGRAMMING PCM I/O + +The I/O interface is usually accessed as /dev/audio or /dev/dsp. +Using the standard Open Sound System (OSS) ioctl calls, the sample +rate, number of channels, and sample format may be set within the +limitations described above. The driver supports triggering. It also +supports getting the input and output pointers with one-sample +accuracy. + +The SNDCTL_DSP_GETCAP ioctl returns these capabilities. + + DSP_CAP_DUPLEX - driver supports full duplex. + + DSP_CAP_TRIGGER - driver supports triggering. + + DSP_CAP_REALTIME - values returned by SNDCTL_DSP_GETIPTR + and SNDCTL_DSP_GETOPTR are accurate to a few samples. + +Memory mapping (mmap) is not implemented. + +The driver permits subdivided fragment sizes from 64 to 4096 bytes. +The number of fragments can be anything from 3 fragments to however +many fragments fit into 124 kilobytes. It is up to the user to +determine how few/small fragments can be used without introducing +glitches with a given workload. Linux is not realtime, so we can't +promise anything. (sigh...) + +When this driver is switched into or out of mu-Law or A-Law mode on +output, it may produce an audible click. This is unavoidable. To +prevent clicking, use signed 16-bit mode instead, and convert from +mu-Law or A-Law format in software. + +============================================================================== +PROGRAMMING THE MIXER INTERFACE + +The mixer interface is usually accessed as /dev/mixer. It is accessed +through ioctls. The mixer allows the application to control gain or +mute several audio signal paths, and also allows selection of the +recording source. + +Each of the constants described here can be read using the +MIXER_READ(SOUND_MIXER_xxx) ioctl. Those that are not read-only can +also be written using the MIXER_WRITE(SOUND_MIXER_xxx) ioctl. In most +cases, defines constants SOUND_MIXER_READ_xxx and +SOUND_MIXER_WRITE_xxx which work just as well. + +SOUND_MIXER_CAPS Read-only + +This is a mask of optional driver capabilities that are implemented. +This driver's only capability is SOUND_CAP_EXCL_INPUT, which means +that only one recording source can be active at a time. + +SOUND_MIXER_DEVMASK Read-only + +This is a mask of the sound channels. This driver's channels are PCM, +LINE, MIC, CD, and RECLEV. + +SOUND_MIXER_STEREODEVS Read-only + +This is a mask of which sound channels are capable of stereo. All +channels are capable of stereo. (But see caveat on MIC input in I/O +CONNECTIONS section above). + +SOUND_MIXER_OUTMASK Read-only + +This is a mask of channels that route inputs through to outputs. +Those are LINE, MIC, and CD. + +SOUND_MIXER_RECMASK Read-only + +This is a mask of channels that can be recording sources. Those are +PCM, LINE, MIC, CD. + +SOUND_MIXER_PCM Default: 0x5757 (0 dB) + +This is the gain control for PCM output. The left and right channel +gain are controlled independently. This gain control has 64 levels, +which range from -82.5 dB to +12.0 dB in 1.5 dB steps. Those 64 +levels are mapped onto 100 levels at the ioctl, see below. + +SOUND_MIXER_LINE Default: 0x4a4a (0 dB) + +This is the gain control for mixing the Line In source into the +outputs. The left and right channel gain are controlled +independently. This gain control has 32 levels, which range from +-34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto +100 levels at the ioctl, see below. + +SOUND_MIXER_MIC Default: 0x4a4a (0 dB) + +This is the gain control for mixing the MIC source into the outputs. +The left and right channel gain are controlled independently. This +gain control has 32 levels, which range from -34.5 dB to +12.0 dB in +1.5 dB steps. Those 32 levels are mapped onto 100 levels at the +ioctl, see below. + +SOUND_MIXER_CD Default: 0x4a4a (0 dB) + +This is the gain control for mixing the CD audio source into the +outputs. The left and right channel gain are controlled +independently. This gain control has 32 levels, which range from +-34.5 dB to +12.0 dB in 1.5 dB steps. Those 32 levels are mapped onto +100 levels at the ioctl, see below. + +SOUND_MIXER_RECLEV Default: 0 (0 dB) + +This is the gain control for PCM input (RECording LEVel). The left +and right channel gain are controlled independently. This gain +control has 16 levels, which range from 0 dB to +22.5 dB in 1.5 dB +steps. Those 16 levels are mapped onto 100 levels at the ioctl, see +below. + +SOUND_MIXER_RECSRC Default: SOUND_MASK_LINE + +This is a mask of currently selected PCM input sources (RECording +SouRCes). Because the AD1843 can only have a single recording source +at a time, only one bit at a time can be set in this mask. The +allowable values are SOUND_MASK_PCM, SOUND_MASK_LINE, SOUND_MASK_MIC, +or SOUND_MASK_CD. Selecting SOUND_MASK_PCM sets up internal +resampling which is useful for loopback testing and for hardware +sample rate conversion. But software sample rate conversion is +probably faster, so I don't know how useful that is. + +SOUND_MIXER_OUTSRC DEFAULT: SOUND_MASK_LINE|SOUND_MASK_MIC|SOUND_MASK_CD + +This is a mask of sources that are currently passed through to the +outputs. Those sources whose bits are not set are muted. + +============================================================================== +GAIN CONTROL + +There are five gain controls listed above. Each has 16, 32, or 64 +steps. Each control has 1.5 dB of gain per step. Each control is +stereo. + +The OSS defines the argument to a channel gain ioctl as having two +components, left and right, each of which ranges from 0 to 100. The +two components are packed into the same word, with the left side gain +in the least significant byte, and the right side gain in the second +least significant byte. In C, we would say this. + + #include + + ... + + assert(leftgain >= 0 && leftgain <= 100); + assert(rightgain >= 0 && rightgain <= 100); + arg = leftgain | rightgain << 8; + +So each OSS gain control has 101 steps. But the hardware has 16, 32, +or 64 steps. The hardware steps are spread across the 101 OSS steps +nearly evenly. The conversion formulas are like this, given N equals +16, 32, or 64. + + int round = N/2 - 1; + OSS_gain_steps = (hw_gain_steps * 100 + round) / (N - 1); + hw_gain_steps = (OSS_gain_steps * (N - 1) + round) / 100; + +Here is a snippet of C code that will return the left and right gain +of any channel in dB. Pass it one of the predefined gain_desc_t +structures to access any of the five channels' gains. + + typedef struct gain_desc { + float min_gain; + float gain_step; + int nbits; + int chan; + } gain_desc_t; + + const gain_desc_t gain_pcm = { -82.5, 1.5, 6, SOUND_MIXER_PCM }; + const gain_desc_t gain_line = { -34.5, 1.5, 5, SOUND_MIXER_LINE }; + const gain_desc_t gain_mic = { -34.5, 1.5, 5, SOUND_MIXER_MIC }; + const gain_desc_t gain_cd = { -34.5, 1.5, 5, SOUND_MIXER_CD }; + const gain_desc_t gain_reclev = { 0.0, 1.5, 4, SOUND_MIXER_RECLEV }; + + int get_gain_dB(int fd, const gain_desc_t *gp, + float *left, float *right) + { + int word; + int lg, rg; + int mask = (1 << gp->nbits) - 1; + + if (ioctl(fd, MIXER_READ(gp->chan), &word) != 0) + return -1; /* fail */ + lg = word & 0xFF; + rg = word >> 8 & 0xFF; + lg = (lg * mask + mask / 2) / 100; + rg = (rg * mask + mask / 2) / 100; + *left = gp->min_gain + gp->gain_step * lg; + *right = gp->min_gain + gp->gain_step * rg; + return 0; + } + +And here is the corresponding routine to set a channel's gain in dB. + + int set_gain_dB(int fd, const gain_desc_t *gp, float left, float right) + { + float max_gain = + gp->min_gain + (1 << gp->nbits) * gp->gain_step; + float round = gp->gain_step / 2; + int mask = (1 << gp->nbits) - 1; + int word; + int lg, rg; + + if (left < gp->min_gain || right < gp->min_gain) + return EINVAL; + lg = (left - gp->min_gain + round) / gp->gain_step; + rg = (right - gp->min_gain + round) / gp->gain_step; + if (lg >= (1 << gp->nbits) || rg >= (1 << gp->nbits)) + return EINVAL; + lg = (100 * lg + mask / 2) / mask; + rg = (100 * rg + mask / 2) / mask; + word = lg | rg << 8; + + return ioctl(fd, MIXER_WRITE(gp->chan), &word); + } + diff -u --recursive --new-file v2.2.11/linux/Documentation/sysrq.txt linux/Documentation/sysrq.txt --- v2.2.11/linux/Documentation/sysrq.txt Mon Aug 9 16:05:54 1999 +++ linux/Documentation/sysrq.txt Wed Aug 25 17:29:45 1999 @@ -21,6 +21,8 @@ On SPARC - You press 'ALT-STOP-', I believe. +On PowerPC - You press 'ALT-Print Screen-'. + On other - If you know of the key combos for other architectures, please let me know so I can add them to this section. diff -u --recursive --new-file v2.2.11/linux/Documentation/video4linux/README.buz linux/Documentation/video4linux/README.buz --- v2.2.11/linux/Documentation/video4linux/README.buz Wed Dec 31 16:00:00 1969 +++ linux/Documentation/video4linux/README.buz Wed Aug 25 17:29:45 1999 @@ -0,0 +1,212 @@ +Iomega Buz Driver for Linux +=========================== + +by Rainer Johanni + +Compiling and Loading the Driver +================================ + +You must run a 2.2.x kernel in order to use this driver. + +To compile the driver, just type make. + +Besides the files in this directory, the driver needs the +'videodev' and the 'i2c' module from the Linux kernel. +In order to get these modules available, enable module support +for VIDEODEV and BTTV (which implies i2c) in your kernel +configuration. You find these devices in the menu +"Character Devices" in your Kernel Configuration. + +Before you load the driver you must have a video device +at major device node 81. If you don't have it yet, do the +following (as root!): + +cd /dev +mknod video0 c 81 0 +ln -s video0 video + +Edit the 'update' script if you want to give the driver +special options and then type (as root) + +./update + +to insert all the necessary modules into the kernel. + +If you want to make full use of the Video for Linux uncompressed +grabbing facilities, you must either + +- obtain and install the "big_physarea patch" for your kernel and + set aside the necessary memory during boot time. + There seem to be several versions of this patch against + various kernel versions floating around in the net, + you may obtain one e.g. from: + http://www.polyware.nl/~middelin/patch/bigphysarea-2.2.1.tar.gz + You also have to compile your driber AFTER installing that patch + in order to get it working + + or + +- start your kernel with the mem=xxx option, where xxx is your + real memory minus the memory needed for the buffers. + For doing this add an entry in lilo.conf (if you use lilo): + append "mem=xxxM" + or add a line in your linux.par file (if you use loadlin): + mem=xxxM + +The second method is by far easier, however it is dangerous +if more than one driver at a time has the idea to use the memory +leftover by setting the mem=xxx parameter below the actual +memory size. + +Read also below how to use this memory! + + + +Driver Options +============== + +You are able to customize the behavior of the driver by giving +it some options at start time. + +default_input, default_norm +--------------------------- + +As soon as the driver is loaded, the Buz samples video signals +from one of its input ports and displays it on its output. +The driver uses the Composite Input and the video norm PAL for this. +If you want to change this default behavior, set default_input=1 +(for S-VHS input) or default_norm=1 for NTSC. + +v4l_nbufs, v4l_bufsize +---------------------- + +In order to make to make full use of the Video for Linux picture +grabbing facilities of the driver (which are needed by many +Video for Linux applications), the driver needs a set of +physically contiguous buffers for grabbing. These parameters +determine how many buffers of which size the driver will +allocate at open (the open will fail if it is unable to do so!). + +These values do not affect the MJPEG grabbing facilities of the driver, +they are needed for uncompressed image grabbing only!!! + +v4l_nbufs is the number of buffers to allocate, a value of 2 (the default) +should be sufficient in allmost all cases. Only special applications +(streaming captures) will need more buffers and then mostly the +MJPEG capturing features of the Buz will be more apropriate. +So leave this parameter at it's default unless you know what you do. + +The things for v4l_bufsize are more complicated: +v4l_bufsize is set by default to 128 [KB] which is the maximum +amount of physically contiguous memory Linux is able to allocate +without kernel changes. This is sufficient for grabbing 24 bit color images +up to sizes of approx. 240x180 pixels (240*180*3 = 129600, 128 KB = 131072). + +In order to be able to capture bigger images you have either to +- obtain and install the "big_physarea patch" and set aside + the necessary memory during boot time or +- start your kernel with the mem=xxx option, where xxx is your + real memory minus the memory needed for the buffers. +In that case, usefull settings for v4l_bufsize are +- 1296 [Kb] for grabbing 24 bit images of max size 768*576 +- 1728 [Kb] for 32bit images of same size (4*768*576 = 1728 Kb!) +You may reduce these numbers accordingly if you know you are only +grabbing 720 pixels wide images or NTSC images (max height 480). + +In some cases it may happen that Linux isn't even able to obtain +the default 128 KB buffers. If you don't need uncompressed image +grabbing at all, set v4l_bufsize to an arbitrary small value (e.g. 4) +in order to be able to open the video device. + +vidmem +------ + +The video mem address of the video card. +The driver has a little database for some videocards +to determine it from there. If your video card is not in there +you have either to give it to the driver as a parameter +or set in in a VIDIOCSFBUF ioctl + +The videocard database is contained in the file "videocards.h" +Gernot Ziegler wants to keep an actual version of that file. +If your card is not contained in that file, look at +http://www.lysator.liu.se/~gz/buz/ for an actual version of +"videocards.h". + +triton, natoma +-------------- + +The driver tries to detect if you have a triton or natome chipset +in order to take special messures for these chipsets. +If this detection fails but you are sure you have such a chipset, +set the corresponding variable to 1. +This is a very special option and may go away in the future. + + + +Programming interface +===================== + +This driver should be fully compliant to Video for Linux, so all +tools working with Video for Linux should work with (hopefully) +no problems. + +A description of the Video for Linux programming interace can be found at: +http://roadrunner.swansea.linux.org.uk/v4lapi.shtml + +Besides the Video for Linux interface, the driver has a "proprietary" +interface for accessing the Buz's MJPEG capture and playback facilities. + +The ioctls for that interface are as follows: + +BUZIOC_G_PARAMS +BUZIOC_S_PARAMS + +Get and set the parameters of the buz. The user should allways +do a BUZIOC_G_PARAMS (with a struct buz_params) to obtain the default +settings, change what he likes and then make a BUZIOC_S_PARAMS call. +A typical application should at least set the members +input, norm and decimation of the struct buz_params. +For a full description of all members see "buz.h" + +BUZIOC_REQBUFS + +Before being able to capture/playback, the user has to request +the buffers he is wanting to use. Fill the structure +buz_requestbuffers with the size (recommended: 256*1024) and +the number (recommended 32 up to 256). There are no such restrictions +as for the Video for Linux buffers, you should LEAVE SUFFICIENT +MEMORY for your system however, else strange things will happen .... +On return, the buz_requestbuffers structure contains number and +size of the actually allocated buffers. +You should use these numbers for doing a mmap of the buffers +into the user space. +The BUZIOC_REQBUFS ioctl also makes it happen, that the next mmap +maps the MJPEG buffer instead of the V4L buffers. + +BUZIOC_QBUF_CAPT +BUZIOC_QBUF_PLAY + +Queue a buffer for capture or playback. The first call also starts +streaming capture. When streaming capture is going on, you may +only queue further buffers or issue syncs until streaming +capture is switched off again with a argument of -1 to +a BUZIOC_QBUF_CAPT/BUZIOC_QBUF_PLAY ioctl. + +BUZIOC_SYNC + +Issue this ioctl when all buffers are queued. This ioctl will +block until the first buffer becomes free for saving its +data to disk (after BUZIOC_QBUF_CAPT) or for reuse (after BUZIOC_QBUF_PLAY). + +BUZIOC_G_STATUS + +Get the status of the input lines (video source connected/norm). +This ioctl may be subject to change. + + + + + +See the examples directory delivered with this driver +for actual coding examples! diff -u --recursive --new-file v2.2.11/linux/MAINTAINERS linux/MAINTAINERS --- v2.2.11/linux/MAINTAINERS Mon Aug 9 16:05:54 1999 +++ linux/MAINTAINERS Wed Aug 25 17:29:45 1999 @@ -89,10 +89,9 @@ 8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] P: Paul Gortmaker -M: gpg109@rsphy1.anu.edu.au +M: p_gortmaker@yahoo.com L: linux-net@vger.rutgers.edu S: Maintained -W: http://rsphy1.anu.edu.au/~gpg109/ne2000.html AD1816 SOUND DRIVER P: Thorsten Knabe @@ -176,6 +175,16 @@ L: linux-fbdev@vuser.vu.union.edu S: Maintained +COMPUTONE INTELLIPORT MULTIPORT CARD +P: Doug McNash +P: Michael H. Warfield +M: Doug McNash +M: Michael H. Warfield +W: http://www.computone.com/ +W: http://www.wittsend.com/computone.html +L: linux-computone@lazuli.wittsend.com +S: Supported + CONFIGURE, MENUCONFIG, XCONFIG P: Michael Elizabeth Chastain M: mec@shout.net @@ -660,7 +669,7 @@ REAL TIME CLOCK DRIVER P: Paul Gortmaker -M: gpg109@rsphy1.anu.edu.au +M: p_gortmaker@yahoo.com L: linux-kernel@vger.rutgers.edu S: Maintained diff -u --recursive --new-file v2.2.11/linux/Makefile linux/Makefile --- v2.2.11/linux/Makefile Mon Aug 9 16:05:54 1999 +++ linux/Makefile Wed Aug 25 17:29:45 1999 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 11 +SUBLEVEL = 12 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -156,6 +156,10 @@ ifeq ($(CONFIG_FC4),y) DRIVERS := $(DRIVERS) drivers/fc4/fc4.a +endif + +ifeq ($(CONFIG_NET_FC),y) +DRIVERS := $(DRIVERS) drivers/net/fc/fc.a endif ifdef CONFIG_PPC diff -u --recursive --new-file v2.2.11/linux/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c --- v2.2.11/linux/arch/alpha/kernel/alpha_ksyms.c Sat May 22 13:41:43 1999 +++ linux/arch/alpha/kernel/alpha_ksyms.c Wed Aug 25 17:29:45 1999 @@ -104,7 +104,9 @@ EXPORT_SYMBOL(wrusp); EXPORT_SYMBOL(start_thread); EXPORT_SYMBOL(alpha_read_fp_reg); +EXPORT_SYMBOL(alpha_read_fp_reg_s); EXPORT_SYMBOL(alpha_write_fp_reg); +EXPORT_SYMBOL(alpha_write_fp_reg_s); /* In-kernel system calls. */ EXPORT_SYMBOL(__kernel_thread); @@ -199,3 +201,5 @@ EXPORT_SYMBOL_NOVERS(__remqu); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); + + diff -u --recursive --new-file v2.2.11/linux/arch/alpha/kernel/core_mcpcia.c linux/arch/alpha/kernel/core_mcpcia.c --- v2.2.11/linux/arch/alpha/kernel/core_mcpcia.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/core_mcpcia.c Wed Aug 25 17:29:45 1999 @@ -568,65 +568,65 @@ /* Print PAL fields */ for (i = 0; i < 24; i += 2) { - printk("\tpal temp[%d-%d]\t\t= %16lx %16lx\n\r", + printk("\tpal temp[%d-%d]\t\t= %16lx %16lx\n", i, i+1, frame->paltemp[i], frame->paltemp[i+1]); } for (i = 0; i < 8; i += 2) { - printk("\tshadow[%d-%d]\t\t= %16lx %16lx\n\r", + printk("\tshadow[%d-%d]\t\t= %16lx %16lx\n", i, i+1, frame->shadow[i], frame->shadow[i+1]); } - printk("\tAddr of excepting instruction\t= %16lx\n\r", + printk("\tAddr of excepting instruction\t= %16lx\n", frame->exc_addr); - printk("\tSummary of arithmetic traps\t= %16lx\n\r", + printk("\tSummary of arithmetic traps\t= %16lx\n", frame->exc_sum); - printk("\tException mask\t\t\t= %16lx\n\r", + printk("\tException mask\t\t\t= %16lx\n", frame->exc_mask); - printk("\tBase address for PALcode\t= %16lx\n\r", + printk("\tBase address for PALcode\t= %16lx\n", frame->pal_base); - printk("\tInterrupt Status Reg\t\t= %16lx\n\r", + printk("\tInterrupt Status Reg\t\t= %16lx\n", frame->isr); - printk("\tCURRENT SETUP OF EV5 IBOX\t= %16lx\n\r", + printk("\tCURRENT SETUP OF EV5 IBOX\t= %16lx\n", frame->icsr); - printk("\tI-CACHE Reg %s parity error\t= %16lx\n\r", + printk("\tI-CACHE Reg %s parity error\t= %16lx\n", (frame->ic_perr_stat & 0x800L) ? "Data" : "Tag", frame->ic_perr_stat); - printk("\tD-CACHE error Reg\t\t= %16lx\n\r", + printk("\tD-CACHE error Reg\t\t= %16lx\n", frame->dc_perr_stat); if (frame->dc_perr_stat & 0x2) { switch (frame->dc_perr_stat & 0x03c) { case 8: - printk("\t\tData error in bank 1\n\r"); + printk("\t\tData error in bank 1\n"); break; case 4: - printk("\t\tData error in bank 0\n\r"); + printk("\t\tData error in bank 0\n"); break; case 20: - printk("\t\tTag error in bank 1\n\r"); + printk("\t\tTag error in bank 1\n"); break; case 10: - printk("\t\tTag error in bank 0\n\r"); + printk("\t\tTag error in bank 0\n"); break; } } - printk("\tEffective VA\t\t\t= %16lx\n\r", + printk("\tEffective VA\t\t\t= %16lx\n", frame->va); - printk("\tReason for D-stream\t\t= %16lx\n\r", + printk("\tReason for D-stream\t\t= %16lx\n", frame->mm_stat); - printk("\tEV5 SCache address\t\t= %16lx\n\r", + printk("\tEV5 SCache address\t\t= %16lx\n", frame->sc_addr); - printk("\tEV5 SCache TAG/Data parity\t= %16lx\n\r", + printk("\tEV5 SCache TAG/Data parity\t= %16lx\n", frame->sc_stat); - printk("\tEV5 BC_TAG_ADDR\t\t\t= %16lx\n\r", + printk("\tEV5 BC_TAG_ADDR\t\t\t= %16lx\n", frame->bc_tag_addr); - printk("\tEV5 EI_ADDR: Phys addr of Xfer\t= %16lx\n\r", + printk("\tEV5 EI_ADDR: Phys addr of Xfer\t= %16lx\n", frame->ei_addr); - printk("\tFill Syndrome\t\t\t= %16lx\n\r", + printk("\tFill Syndrome\t\t\t= %16lx\n", frame->fill_syndrome); - printk("\tEI_STAT reg\t\t\t= %16lx\n\r", + printk("\tEI_STAT reg\t\t\t= %16lx\n", frame->ei_stat); - printk("\tLD_LOCK\t\t\t\t= %16lx\n\r", + printk("\tLD_LOCK\t\t\t\t= %16lx\n", frame->ld_lock); } @@ -657,7 +657,8 @@ process_mcheck_info(vector, la_ptr, regs, "MCPCIA", DEBUG_MCHECK, MCPCIA_mcheck_expected[cpu]); - if (vector != 0x620 && vector != 0x630) { + if (vector != 0x620 && vector != 0x630 + && ! MCPCIA_mcheck_expected[cpu]) { mcpcia_print_uncorrectable(mchk_logout); } diff -u --recursive --new-file v2.2.11/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c --- v2.2.11/linux/arch/alpha/kernel/process.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/process.c Wed Aug 25 17:29:45 1999 @@ -55,7 +55,6 @@ unsigned long init_user_stack[1024] = { STACK_MAGIC, }; static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; @@ -89,6 +88,7 @@ /* Although we are an idle CPU, we do not want to get into the scheduler unnecessarily. */ + barrier(); if (current->need_resched) { schedule(); check_pgt_cache(); diff -u --recursive --new-file v2.2.11/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c --- v2.2.11/linux/arch/alpha/kernel/setup.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/kernel/setup.c Wed Aug 25 17:29:45 1999 @@ -340,8 +340,11 @@ high = (high + PAGE_SIZE) & (PAGE_MASK*2); /* Enforce maximum of 2GB even if there is more. Blah. */ - if (high > 0x80000000UL) + if (high > 0x80000000UL) { + printk("Cropping memory from %luMB to 2048MB\n", high); high = 0x80000000UL; + } + return PAGE_OFFSET + high; } diff -u --recursive --new-file v2.2.11/linux/arch/alpha/math-emu/Makefile linux/arch/alpha/math-emu/Makefile --- v2.2.11/linux/arch/alpha/math-emu/Makefile Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/math-emu/Makefile Wed Aug 25 17:29:45 1999 @@ -2,19 +2,11 @@ # Makefile for math-emulator files... # -O_TARGET := math-emu.o -O_OBJS := fp-emul.o faddd.o fadds.o fdivd.o fdivs.o fdtoi.o \ - fdtos.o fdtox.o fmuld.o fmuls.o fsmuld.o fsqrtd.o \ - fsqrts.o fstod.o fstoi.o fstox.o fsubd.o fsubs.o \ - cmptxx.o fxtos.o fxtod.o udivmodti4.o div128.o - -LINKS := double.h faddd.c fadds.c fdivd.c fdivs.c fdtoi.c \ - fdtos.c fdtox.c fmuld.c fmuls.c fsmuld.c fsqrtd.c \ - fsqrts.c fstod.c fstoi.c fstox.c fsubd.c fsubs.c \ - op-common.h op-1.h op-2.h op-4.h single.h soft-fp.h \ - udivmodti4.c - +O_TARGET := math-emu.o +O_OBJS := fp-emul.o udivmodti4.o +LINKS := double.h op-common.h op-1.h op-2.h op-4.h single.h \ + soft-fp.h udivmodti4.c ifeq ($(CONFIG_MATHEMU),m) M_OBJS := $(O_TARGET) diff -u --recursive --new-file v2.2.11/linux/arch/alpha/math-emu/cmptxx.c linux/arch/alpha/math-emu/cmptxx.c --- v2.2.11/linux/arch/alpha/math-emu/cmptxx.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/math-emu/cmptxx.c Wed Dec 31 16:00:00 1969 @@ -1,23 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int CMPTXX(void *rc, void *rb, void *ra, int type) -{ - FP_DECL_D(A); FP_DECL_D(B); - long ret; - - __FP_UNPACK_D(A, ra); - __FP_UNPACK_D(B, rb); - FP_CMP_D(ret, A, B, 3); - if(ret == type) { - *(unsigned long *)rc = 0x4000000000000000; - } - else if((type == CMPTXX_LE) && - ((ret == CMPTXX_LT) || (ret == CMPTXX_EQ))) { - *(unsigned long *)rc = 0x4000000000000000; - } - else { - *(unsigned long *)rc = 0; - } - return 0; -} diff -u --recursive --new-file v2.2.11/linux/arch/alpha/math-emu/div128.c linux/arch/alpha/math-emu/div128.c --- v2.2.11/linux/arch/alpha/math-emu/div128.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/math-emu/div128.c Wed Dec 31 16:00:00 1969 @@ -1,125 +0,0 @@ -/* - Copyright stuff - - Use of this program, for any purpose, is granted the author, - Ian Kaplan, as long as this copyright notice is included in - the source code or any source code derived from this program. - The user assumes all responsibility for using this code. - - Ian Kaplan, October 1996 - -*/ - -#define HI 0 -#define LO 1 - -void set128(unsigned long *n, unsigned long hi, unsigned long lo) -{ - n[HI] = hi; - n[LO] = lo; -} - -int eq128(unsigned long *n1, unsigned long *n2) -{ - return((n1[HI] == n2[HI]) && (n1[LO] == n2[LO])); -} - -int gt128(unsigned long *n1, unsigned long *n2) -{ - return((n1[HI] > n2[HI]) || - ((n1[HI] == n2[HI]) && (n1[LO] > n2[LO]))); -} - -int lt128(unsigned long *n1, unsigned long *n2) -{ - return((n1[HI] < n2[HI]) || - ((n1[HI] == n2[HI]) && (n1[LO] < n2[LO]))); -} - - -void copy128(unsigned long *dest, unsigned long *src) -{ - dest[HI] = src[HI]; - dest[LO] = src[LO]; -} - -/* Shift the given bit into the octaword from the right - * (i.e. left-shift-1, or in low bit). If "bit" is zero, - * then this is a simple left shift. - */ -void shiftin128(unsigned long *n, unsigned long bit) -{ - n[HI] <<= 1; - if(n[LO] & 0x8000000000000000) { - n[HI] |= 1; - } - n[LO] = (n[LO] << 1) | bit; -} - -void sub128(unsigned long *n1, unsigned long *n2, unsigned long *result) -{ - if(n1[LO] < n2[LO]) { - result[LO] = n1[LO] - n2[LO]; - result[HI] = n1[HI] - n2[HI] - 1; - } - else { - result[LO] = n1[LO] - n2[LO]; - result[HI] = n1[HI] - n2[HI]; - } -} - - -void udiv128(unsigned long *dividend, - unsigned long *divisor, - unsigned long *quotient, - unsigned long *remainder ) -{ - unsigned long zero[2]; - unsigned long t[2], num_bits; - unsigned long q, bit; - unsigned long rem[2]; - int i; - - set128(remainder, 0, 0); - set128(quotient, 0, 0); - set128(zero, 0, 0); - - if (eq128(divisor, zero)) { - return; - } - - if(gt128(divisor, dividend)) { - copy128(remainder, dividend); - return; - } - - if (eq128(divisor, dividend)) { - set128(quotient, 0, 1); - return; - } - - num_bits = 128; - - while(1) { - bit = (dividend[HI] & 0x8000000000000000) >> 63; - copy128(rem, remainder); - shiftin128(rem, bit); - if(lt128(rem, divisor)) break; - copy128(remainder, rem); - shiftin128(dividend, 0); - num_bits--; - } - - for (i = 0; i < num_bits; i++) { - bit = (dividend[HI] & 0x8000000000000000) >> 63; - shiftin128(remainder, bit); - sub128(remainder, divisor, t); - q = !((t[HI] & 0x8000000000000000) >> 63); - shiftin128(dividend, 0); - shiftin128(quotient, q); - if (q) { - copy128(remainder, t); - } - } -} /* unsigned_divide128 */ - diff -u --recursive --new-file v2.2.11/linux/arch/alpha/math-emu/fp-emul.c linux/arch/alpha/math-emu/fp-emul.c --- v2.2.11/linux/arch/alpha/math-emu/fp-emul.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/math-emu/fp-emul.c Wed Aug 25 17:29:45 1999 @@ -6,26 +6,10 @@ #include #include "soft-fp.h" - -extern int CMPTXX(void *, void *, void *, int); -extern int FXTOS(void *, void *); -extern int FXTOD(void *, void *); -extern int FDTOS(void *, void *); -extern int FSTOD(void *, void *); -extern int FDIVS(void *, void *, void *); -extern int FDIVD(void *, void *, void *); -extern int FMULS(void *, void *, void *); -extern int FMULD(void *, void *, void *); -extern int FSUBS(void *, void *, void *); -extern int FSUBD(void *, void *, void *); -extern int FADDS(void *, void *, void *); -extern int FADDD(void *, void *, void *); -extern int FDTOX(void *, void *); -extern int FSQRTS(void *, void *); -extern int FSQRTD(void *, void *); +#include "double.h" +#include "single.h" #define OPC_PAL 0x00 - #define OPC_INTA 0x10 #define OPC_INTL 0x11 #define OPC_INTS 0x12 @@ -34,42 +18,26 @@ #define OPC_FLTV 0x15 #define OPC_FLTI 0x16 #define OPC_FLTL 0x17 - #define OPC_MISC 0x18 - #define OPC_JSR 0x1a -#define OP_FUN(OP,FUN) ((OP << 26) | (FUN << 5)) - -/* - * "Base" function codes for the FLTI-class instructions. - * Note that in most cases these actually correspond to the "chopped" - * form of the instruction. Not to worry---we extract the qualifier - * bits separately and deal with them separately. Notice that base - * function code 0x2c is used for both CVTTS and CVTST. The other bits - * in the function code are used to distinguish the two. - */ -#define FLTI_FUNC_ADDS OP_FUN(OPC_FLTI, 0x000) -#define FLTI_FUNC_ADDT OP_FUN(OPC_FLTI, 0x020) -#define FLTI_FUNC_CMPTEQ OP_FUN(OPC_FLTI, 0x025) -#define FLTI_FUNC_CMPTLT OP_FUN(OPC_FLTI, 0x026) -#define FLTI_FUNC_CMPTLE OP_FUN(OPC_FLTI, 0x027) -#define FLTI_FUNC_CMPTUN OP_FUN(OPC_FLTI, 0x024) -#define FLTI_FUNC_CVTTS_or_CVTST OP_FUN(OPC_FLTI, 0x02c) -#define FLTI_FUNC_CVTTQ OP_FUN(OPC_FLTI, 0x02f) -#define FLTI_FUNC_CVTQS OP_FUN(OPC_FLTI, 0x03c) -#define FLTI_FUNC_CVTQT OP_FUN(OPC_FLTI, 0x03e) -#define FLTI_FUNC_DIVS OP_FUN(OPC_FLTI, 0x003) -#define FLTI_FUNC_DIVT OP_FUN(OPC_FLTI, 0x023) -#define FLTI_FUNC_MULS OP_FUN(OPC_FLTI, 0x002) -#define FLTI_FUNC_MULT OP_FUN(OPC_FLTI, 0x022) -#define FLTI_FUNC_SUBS OP_FUN(OPC_FLTI, 0x001) -#define FLTI_FUNC_SUBT OP_FUN(OPC_FLTI, 0x021) - -#define FLTC_FUNC_SQRTS OP_FUN(OPC_FLTC, 0x00B) -#define FLTC_FUNC_SQRTT OP_FUN(OPC_FLTC, 0x02B) - -#define FLTL_FUNC_CVTQL OP_FUN(OPC_FLTL, 0x030) +#define FOP_SRC_S 0 +#define FOP_SRC_T 2 +#define FOP_SRC_Q 3 + +#define FOP_FNC_ADDx 0 +#define FOP_FNC_CVTQL 0 +#define FOP_FNC_SUBx 1 +#define FOP_FNC_MULx 2 +#define FOP_FNC_DIVx 3 +#define FOP_FNC_CMPxUN 4 +#define FOP_FNC_CMPxEQ 5 +#define FOP_FNC_CMPxLT 6 +#define FOP_FNC_CMPxLE 7 +#define FOP_FNC_SQRTx 11 +#define FOP_FNC_CVTxS 12 +#define FOP_FNC_CVTxT 14 +#define FOP_FNC_CVTxQ 15 #define MISC_TRAPB 0x0000 #define MISC_EXCB 0x0400 @@ -115,6 +83,67 @@ #endif /* MODULE */ +/* For 128-bit division. */ + +__complex__ unsigned long +udiv128(unsigned long divisor_f0, unsigned long divisor_f1, + unsigned long dividend_f0, unsigned long dividend_f1) +{ + _FP_FRAC_DECL_2(quo); + _FP_FRAC_DECL_2(rem); + _FP_FRAC_DECL_2(tmp); + unsigned long i, num_bits, bit; + __complex__ unsigned long ret; + + _FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2); + _FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2); + + if (_FP_FRAC_ZEROP_2(divisor)) + goto out; + + if (_FP_FRAC_GT_2(divisor, dividend)) { + _FP_FRAC_COPY_2(rem, dividend); + goto out; + } + + if (_FP_FRAC_EQ_2(divisor, dividend)) { + __FP_FRAC_SET_2(quo, 0, 1); + goto out; + } + + num_bits = 128; + while (1) { + bit = _FP_FRAC_NEGP_2(dividend); + _FP_FRAC_COPY_2(tmp, rem); + _FP_FRAC_SLL_2(tmp, 1); + _FP_FRAC_LOW_2(tmp) |= bit; + if (! _FP_FRAC_GE_2(tmp, divisor)) + break; + _FP_FRAC_COPY_2(rem, tmp); + _FP_FRAC_SLL_2(dividend, 1); + num_bits--; + } + + for (i = 0; i < num_bits; i++) { + bit = _FP_FRAC_NEGP_2(dividend); + _FP_FRAC_SLL_2(rem, 1); + _FP_FRAC_LOW_2(rem) |= bit; + _FP_FRAC_SUB_2(tmp, rem, divisor); + bit = _FP_FRAC_NEGP_2(tmp); + _FP_FRAC_SLL_2(dividend, 1); + _FP_FRAC_SLL_2(quo, 1); + if (!bit) { + _FP_FRAC_LOW_2(quo) |= 1; + _FP_FRAC_COPY_2(rem, tmp); + } + } + +out: + __real__ ret = quo_f1; + __imag__ ret = rem_f1; + return ret; +} + /* * Emulate the floating point instruction at address PC. Returns 0 if * emulation fails. Notice that the kernel does not and cannot use FP @@ -125,195 +154,185 @@ long alpha_fp_emul (unsigned long pc) { - unsigned long op_fun, fa, fb, fc, func, mode; + FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); + FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); + + unsigned long fa, fb, fc, func, mode, src; unsigned long fpcw = current->tss.flags; - unsigned long va, vb, vc, res, fpcr; + unsigned long res, cmptype, va, vb, vc, fpcr; __u32 insn; MOD_INC_USE_COUNT; get_user(insn, (__u32*)pc); - fc = (insn >> 0) & 0x1f; /* destination register */ - fb = (insn >> 16) & 0x1f; - fa = (insn >> 21) & 0x1f; - func = (insn >> 5) & 0x7ff; - mode = (insn >> 11) & 0x3; - op_fun = insn & OP_FUN(0x3f, 0x3f); + fc = (insn >> 0) & 0x1f; /* destination register */ + fb = (insn >> 16) & 0x1f; + fa = (insn >> 21) & 0x1f; + func = (insn >> 5) & 0xf; + src = (insn >> 9) & 0x3; + mode = (insn >> 11) & 0x3; fpcr = rdfpcr(); - /* - * Try the operation in software. First, obtain the rounding - * mode and set it in the task struct - */ - current->tss.flags &= ~IEEE_CURRENT_RM_MASK; if (mode == 3) { - /* dynamic---get rounding mode from fpcr: */ - current->tss.flags |= - (((fpcr&FPCR_DYN_MASK)>>FPCR_DYN_SHIFT)<tss.flags |= (mode << IEEE_CURRENT_RM_SHIFT); + /* Dynamic -- get rounding mode from fpcr. */ + mode = (fpcr >> FPCR_DYN_SHIFT) & 3; } - /* JRP - What is this test supposed to check for? */ - if ((IEEE_TRAP_ENABLE_MASK & 0x80 /* was 0xc0 */)) { - extern int something_is_wrong (void); - something_is_wrong(); - } + res = 0; - switch (op_fun) { - case FLTI_FUNC_CMPTEQ: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = CMPTXX(&vc, &vb, &va, CMPTXX_EQ); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CMPTLT: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = CMPTXX(&vc, &vb, &va, CMPTXX_LT); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CMPTLE: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = CMPTXX(&vc, &vb, &va, CMPTXX_LE); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CMPTUN: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = CMPTXX(&vc, &vb, &va, CMPTXX_UN); - alpha_write_fp_reg(fc, vc); - break; - - case FLTL_FUNC_CVTQL: - /* - * Notice: We can get here only due to an integer - * overflow. Such overflows are reported as invalid - * ops. We return the result the hw would have - * computed. - */ - vb = alpha_read_fp_reg(fb); - vc = ((vb & 0xc0000000) << 32 | /* sign and msb */ - (vb & 0x3fffffff) << 29); /* rest of the integer */ - res = EFLAG_INVALID; - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CVTQS: - vb = alpha_read_fp_reg(fb); - res = FXTOS(&vc, &vb); - alpha_write_fp_reg_s(fc, vc); - break; - - case FLTI_FUNC_CVTQT: - vb = alpha_read_fp_reg(fb); - res = FXTOD(&vc, &vb); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_CVTTS_or_CVTST: - if (func == 0x6ac) { - /* - * 0x2ac is also CVTST, but if the /S - * qualifier isn't set, we wouldn't be here in - * the first place... - */ - vb = alpha_read_fp_reg_s(fb); - res = FSTOD(&vc, &vb); - alpha_write_fp_reg(fc, vc); - } else { - vb = alpha_read_fp_reg(fb); - res = FDTOS(&vc, &vb); - alpha_write_fp_reg_s(fc, vc); - } - break; - - case FLTI_FUNC_DIVS: - va = alpha_read_fp_reg_s(fa); - vb = alpha_read_fp_reg_s(fb); - res = FDIVS(&vc, &vb, &va); - alpha_write_fp_reg_s(fc, vc); - break; - - case FLTI_FUNC_DIVT: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = FDIVD(&vc, &vb, &va); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_MULS: - va = alpha_read_fp_reg_s(fa); - vb = alpha_read_fp_reg_s(fb); - res = FMULS(&vc, &vb, &va); - alpha_write_fp_reg_s(fc, vc); - break; - - case FLTI_FUNC_MULT: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = FMULD(&vc, &vb, &va); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_SUBS: + switch (src) { + case FOP_SRC_S: va = alpha_read_fp_reg_s(fa); vb = alpha_read_fp_reg_s(fb); - res = FSUBS(&vc, &vb, &va); - alpha_write_fp_reg_s(fc, vc); - break; - - case FLTI_FUNC_SUBT: - va = alpha_read_fp_reg(fa); - vb = alpha_read_fp_reg(fb); - res = FSUBD(&vc, &vb, &va); - alpha_write_fp_reg(fc, vc); - break; - - case FLTI_FUNC_ADDS: - va = alpha_read_fp_reg_s(fa); - vb = alpha_read_fp_reg_s(fb); - res = FADDS(&vc, &vb, &va); - alpha_write_fp_reg_s(fc, vc); - break; + + __FP_UNPACK_S(SA, &va); + __FP_UNPACK_S(SB, &vb); + + switch (func) { + case FOP_FNC_SUBx: + if (SB_c != FP_CLS_NAN) + SB_s ^= 1; + /* FALLTHRU */ + case FOP_FNC_ADDx: + FP_ADD_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_MULx: + FP_MUL_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_DIVx: + if (SB_c == FP_CLS_ZERO && SA_c != FP_CLS_ZERO) { + res |= EFLAG_DIVZERO; + if (__FPU_TRAP_P(EFLAG_DIVZERO)) + goto done; + } + FP_DIV_S(SR, SA, SB); + goto pack_s; + + case FOP_FNC_SQRTx: + FP_SQRT_S(SR, SA); + goto pack_s; + } + goto bad_insn; - case FLTI_FUNC_ADDT: + case FOP_SRC_T: va = alpha_read_fp_reg(fa); vb = alpha_read_fp_reg(fb); - res = FADDD(&vc, &vb, &va); - alpha_write_fp_reg(fc, vc); - break; + + __FP_UNPACK_D(DA, &va); + __FP_UNPACK_D(DB, &vb); + + switch (func) { + case FOP_FNC_SUBx: + if (DB_c != FP_CLS_NAN) + DB_s ^= 1; + /* FALLTHRU */ + case FOP_FNC_ADDx: + FP_ADD_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_MULx: + FP_MUL_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_DIVx: + if (DB_c == FP_CLS_ZERO && DA_c != FP_CLS_ZERO) { + res |= EFLAG_DIVZERO; + if (__FPU_TRAP_P(EFLAG_DIVZERO)) + goto done; + } + FP_DIV_D(DR, DA, DB); + goto pack_d; + + case FOP_FNC_CMPxUN: + cmptype = CMPTXX_UN; + goto compare; + case FOP_FNC_CMPxEQ: + cmptype = CMPTXX_EQ; + goto compare; + case FOP_FNC_CMPxLT: + cmptype = CMPTXX_LT; + goto compare; + case FOP_FNC_CMPxLE: + cmptype = CMPTXX_LE; + goto compare; + compare: + FP_CMP_D(res, DA, DB, 3); + vc = 0; + if (res == cmptype + || (cmptype == CMPTXX_LE + && (res == CMPTXX_LT || res == CMPTXX_EQ))) { + vc = 0x4000000000000000; + } + goto done_d; + + case FOP_FNC_SQRTx: + FP_SQRT_D(DR, DA); + goto pack_d; + + case FOP_FNC_CVTxS: + /* It is irritating that DEC encoded CVTST with + SRC == T_floating. It is also interesting that + the bit used to tell the two apart is /U... */ + if (insn & 0x2000) { + FP_CONV(S,D,1,1,SR,DA); + goto pack_s; + } else { + /* CVTST need do nothing else but copy the + bits and repack. */ + DR_c = DA_c; + DR_s = DA_s; + DR_e = DA_e; + DR_r = DA_r; + DR_f = DA_f; + goto pack_d; + } + + case FOP_FNC_CVTxQ: + FP_TO_INT_D(vc, DA, 64, 1); + res = _FTOI_RESULT(DA); + goto done_d; + } + goto bad_insn; - case FLTI_FUNC_CVTTQ: + case FOP_SRC_Q: vb = alpha_read_fp_reg(fb); - res = FDTOX(&vc, &vb); - alpha_write_fp_reg(fc, vc); - break; - - case FLTC_FUNC_SQRTS: - vb = alpha_read_fp_reg_s(fb); - res = FSQRTS(&vc, &vb); - alpha_write_fp_reg_s(fc, vc); - break; - case FLTC_FUNC_SQRTT: - vb = alpha_read_fp_reg(fb); - res = FSQRTD(&vc, &vb); - alpha_write_fp_reg(fc, vc); - break; - - default: - printk("alpha_fp_emul: unexpected function code %#lx at %#lx\n", - func & 0x3f, pc); - MOD_DEC_USE_COUNT; - return 0; + switch (func) { + case FOP_FNC_CVTQL: + /* Notice: We can get here only due to an integer + overflow. Such overflows are reported as invalid + ops. We return the result the hw would have + computed. */ + vc = ((vb & 0xc0000000) << 32 | /* sign and msb */ + (vb & 0x3fffffff) << 29); /* rest of the int */ + res = EFLAG_INVALID; + goto done_d; + + case FOP_FNC_CVTxS: + FP_FROM_INT_S(SR, ((long)vb), 64, long); + goto pack_s; + + case FOP_FNC_CVTxT: + FP_FROM_INT_D(DR, ((long)vb), 64, long); + goto pack_d; + } + goto bad_insn; } + goto bad_insn; + +pack_s: + res |= __FP_PACK_S(&vc, SR); + alpha_write_fp_reg_s(fc, vc); + goto done; + +pack_d: + res |= __FP_PACK_D(&vc, DR); +done_d: + alpha_write_fp_reg(fc, vc); + goto done; /* * Take the appropriate action for each possible @@ -327,9 +346,11 @@ * In addition, properly track the exception state in software * as described in the Alpha Architectre Handbook section 4.7.7.3. */ +done: if (res) { /* Record exceptions in software control word. */ - current->tss.flags = fpcw |= (res << IEEE_STATUS_TO_EXCSUM_SHIFT); + current->tss.flags + = fpcw |= (res << IEEE_STATUS_TO_EXCSUM_SHIFT); /* Update hardware control register */ fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); @@ -343,16 +364,19 @@ } } - /* We used to write the destination register here, but - * DEC FORTRAN requires that the result *always* be - * written... so we do the write immediately after - * the operations above. - */ + /* We used to write the destination register here, but DEC FORTRAN + requires that the result *always* be written... so we do the write + immediately after the operations above. */ MOD_DEC_USE_COUNT; return 1; -} +bad_insn: + printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n", + insn, pc); + MOD_DEC_USE_COUNT; + return 0; +} long alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) diff -u --recursive --new-file v2.2.11/linux/arch/alpha/math-emu/fxtod.c linux/arch/alpha/math-emu/fxtod.c --- v2.2.11/linux/arch/alpha/math-emu/fxtod.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/math-emu/fxtod.c Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "double.h" - -int FXTOD(void *rd, void *rs2) -{ - FP_DECL_D(R); - long a = *(long *)rs2; - - FP_FROM_INT_D(R, a, 64, long); - return __FP_PACK_D(rd, R); -} diff -u --recursive --new-file v2.2.11/linux/arch/alpha/math-emu/fxtos.c linux/arch/alpha/math-emu/fxtos.c --- v2.2.11/linux/arch/alpha/math-emu/fxtos.c Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/math-emu/fxtos.c Wed Dec 31 16:00:00 1969 @@ -1,11 +0,0 @@ -#include "soft-fp.h" -#include "single.h" - -int FXTOS(void *rd, void *rs2) -{ - FP_DECL_S(R); - long a = *(long *)rs2; - - FP_FROM_INT_S(R, a, 64, long); - return __FP_PACK_S(rd, R); -} diff -u --recursive --new-file v2.2.11/linux/arch/alpha/math-emu/sfp-machine.h linux/arch/alpha/math-emu/sfp-machine.h --- v2.2.11/linux/arch/alpha/math-emu/sfp-machine.h Mon Aug 9 16:05:54 1999 +++ linux/arch/alpha/math-emu/sfp-machine.h Wed Aug 25 17:29:45 1999 @@ -38,13 +38,13 @@ #define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0 /* On some architectures float-to-int conversions return a result - * code. On others (e.g. Sparc) they return 0 - */ -#define _FTOI_RESULT A_r + code. On others (e.g. Sparc) they return 0. */ +#define _FTOI_RESULT(X) X##_r -#define _FP_KEEPNANFRACP 1 +#define _FP_KEEPNANFRACP 1 -/* Alpha Architecture Manual Section 4.7.10.4: Propagating NaN Values, +/* + * Alpha Architecture Manual Section 4.7.10.4: Propagating NaN Values, * summary: * * The first of the following rules that is applicable governs the @@ -66,29 +66,25 @@ #define _FP_CHOOSENAN(fs, wc, R, X, Y) \ do { \ R##_r |= (X##_r | Y##_r); \ - if(_FP_IS_NAN(fs, Y)) { \ + if (_FP_IS_NAN(fs, Y)) { \ R##_s = Y##_s; \ R##_c = FP_CLS_NAN; \ - if(_FP_IS_QNAN(fs, Y)) { /* Rule 1 */ \ + if (_FP_IS_QNAN(fs, Y)) { /* Rule 1 */ \ _FP_FRAC_COPY_##wc(R,Y); \ - } \ - else { /* Rule 2 */ \ + } else { /* Rule 2 */ \ _FP_FRAC_SET_##wc(R,Y##_f | _FP_QNANBIT_##fs); \ R##_r = EFLAG_INVALID; \ } \ - } \ - else if(_FP_IS_NAN(fs, X)) { \ + } else if (_FP_IS_NAN(fs, X)) { \ R##_s = X##_s; \ R##_c = FP_CLS_NAN; \ - if(_FP_IS_QNAN(fs, X)) { /* Rule 3 */ \ + if (_FP_IS_QNAN(fs, X)) { /* Rule 3 */ \ _FP_FRAC_COPY_##wc(R,X); \ - } \ - else { /* Rule 4 */ \ + } else { /* Rule 4 */ \ _FP_FRAC_SET_##wc(R,X##_f | _FP_QNANBIT_##fs); \ R##_r |= EFLAG_INVALID; \ } \ - } \ - else { /* Rule 5 */ \ + } else { /* Rule 5 */ \ R##_s = 1; \ R##_c = FP_CLS_NAN; \ _FP_FRAC_SET_##wc(R,_FP_QNANBIT_##fs | EFLAG_MASK); \ @@ -98,18 +94,16 @@ /* Rules 3 and 4 don't apply to functions of only one argument */ #define _FP_CHOOSENAN_1(fs, wc, R, X) \ do { \ - if(_FP_IS_NAN(fs, X)) { \ + if (_FP_IS_NAN(fs, X)) { \ R##_s = X##_s; \ R##_c = FP_CLS_NAN; \ - if(_FP_IS_QNAN(fs, X)) { /* Rule 1 */ \ + if (_FP_IS_QNAN(fs, X)) { /* Rule 1 */ \ _FP_FRAC_COPY_##wc(R,X); \ - } \ - else { /* Rule 2 */ \ + } else { /* Rule 2 */ \ _FP_FRAC_SET_##wc(R,X##_f | _FP_QNANBIT_##fs); \ R##_r |= EFLAG_INVALID; \ } \ - } \ - else { /* Rule 5 */ \ + } else { /* Rule 5 */ \ R##_s = 1; \ R##_c = FP_CLS_NAN; \ _FP_FRAC_SET_##wc(R,_FP_QNANBIT_##fs | EFLAG_MASK); \ @@ -119,45 +113,44 @@ #define _FP_CHOOSENAN_SQRT _FP_CHOOSENAN_1 -#define __FP_UNPACK_DENORM(fs, wc, X) \ - { \ +#define __FP_UNPACK_DENORM(fs, wc, X) \ + do { \ _FP_I_TYPE _shift; \ X##_r |= EFLAG_DENORM; \ - if(_FP_DENORM_TO_ZERO) { \ + if (_FP_DENORM_TO_ZERO) { \ /* Crunching a nonzero denorm to zero necessarily makes */ \ /* the result inexact */ \ X##_r |= EFLAG_INEXACT; \ _FP_FRAC_SET_##wc(X, 0); \ X##_c = FP_CLS_ZERO; \ - } \ - else { \ + } else { \ _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; \ } \ - } + } 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; \ +#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_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; \ +#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_UNPACK_S(X,val) \ @@ -178,25 +171,25 @@ _FP_UNPACK_CANONICAL(Q,2,X); \ } 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; \ +#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_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; \ +#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) @@ -223,7 +216,7 @@ _FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs); \ else if (!_FP_FRAC_ZEROP_##wc(X)) { \ _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \ - R_r |= EFLAG_INEXACT; \ + R##_r |= EFLAG_INEXACT; \ } \ else \ _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ @@ -237,7 +230,7 @@ _FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs); \ else if (!_FP_FRAC_ZEROP_##wc(Y)) { \ _FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc); \ - R_r |= EFLAG_INEXACT; \ + R##_r |= EFLAG_INEXACT; \ } \ else \ _FP_FRAC_SET_##wc(Y, _FP_ZEROFRAC_##wc); \ @@ -412,8 +405,8 @@ #define _FP_DIV(fs, wc, R, X, Y) \ do { \ - /* Propagate any flags that may have been set during unpacking */ \ - R##_r |= (X##_r | Y##_r); \ + /* Propagate any flags that may have been set during unpacking */ \ + R##_r |= (X##_r | Y##_r); \ R##_s = X##_s ^ Y##_s; \ switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ { \ @@ -512,17 +505,14 @@ }) -/* We only actually write to the destination register - * if exceptions signalled (if any) will not trap. - */ -#define __FPU_TEM ((current->tss.flags)& IEEE_TRAP_ENABLE_MASK) - -#define __FPU_TRAP_P(bits) \ - ((__FPU_TEM & (bits)) != 0) +/* We only actually write to the destination register if exceptions + signalled (if any) will not trap. */ +#define __FPU_TEM (current->tss.flags & IEEE_TRAP_ENABLE_MASK) +#define __FPU_TRAP_P(bits) ((__FPU_TEM & (bits)) != 0) #define __FP_PACK_S(val,X) \ ({ int __exc = _FP_PACK_CANONICAL(S,1,X); \ - if(!__exc || !__FPU_TRAP_P(__exc)) \ + if (!__exc || !__FPU_TRAP_P(__exc)) \ __FP_PACK_RAW_1(S,val,X); \ __exc; \ }) @@ -542,9 +532,7 @@ }) /* Obtain the current rounding mode. */ -#define FP_ROUNDMODE \ - (((current->tss.flags)&IEEE_CURRENT_RM_MASK)>>IEEE_CURRENT_RM_SHIFT) - +#define FP_ROUNDMODE mode #define FP_RND_NEAREST (FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT) #define FP_RND_ZERO (FPCR_DYN_CHOPPED >> FPCR_DYN_SHIFT) #define FP_RND_PINF (FPCR_DYN_PLUS >> FPCR_DYN_SHIFT) @@ -552,48 +540,32 @@ #define add_ssaaaa(sh, sl, ah, al, bh, bl) \ - __asm__ ("addq %4,%5,%1; cmpult %1,%4,$28; addq %2,%3,%0; addq %0,$28,%0" \ - : "=r" ((UDItype)(sh)), \ - "=r" ((UDItype)(sl)) \ - : "r" ((UDItype)(ah)), \ - "r" ((UDItype)(bh)), \ - "r" ((UDItype)(al)), \ - "r" ((UDItype)(bl))) - + ((sl) = (al) + (bl), (sh) = (ah) + (bh) + ((sl) < (al))) + #define sub_ddmmss(sh, sl, ah, al, bh, bl) \ - __asm__ ("subq %4,%5,%1; cmpult %4,%5,$28; subq %2,%3,%0; subq %0,$28,%0"\ - : "=r" ((UDItype)(sh)), \ - "=&r" ((UDItype)(sl)) \ - : "r" ((UDItype)(ah)), \ - "r" ((UDItype)(bh)), \ - "r" ((UDItype)(al)), \ - "r" ((UDItype)(bl))) - -#define umul_ppmm(wh, wl, u, v) \ - do { \ - __asm__ ("mulq %2,%3,%1; umulh %2,%3,%0" \ - : "=r" ((UDItype)(wh)), \ - "=&r" ((UDItype)(wl)) \ - : "r" ((UDItype)(u)), \ - "r" ((UDItype)(v))); \ - } while (0) + ((sl) = (al) - (bl), (sh) = (ah) - (bh) - ((al) < (bl))) +#define umul_ppmm(wh, wl, u, v) \ + __asm__ ("mulq %2,%3,%1; umulh %2,%3,%0" \ + : "=r" ((UDItype)(wh)), \ + "=&r" ((UDItype)(wl)) \ + : "r" ((UDItype)(u)), \ + "r" ((UDItype)(v))) -#define udiv_qrnnd(q, r, n1, n0, d) \ - do { \ - unsigned long __n[2]; \ - unsigned long __d[2]; \ - unsigned long __q[2]; \ - unsigned long __r[2]; \ - __n[0]=n1; __n[1]=n0; __d[0]=0; __d[1]=d; \ - udiv128(__n, __d, __q, __r); \ - q=__q[1]; r=__r[1]; \ +extern __complex__ unsigned long udiv128(unsigned long, unsigned long, + unsigned long, unsigned long); + +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + __complex__ unsigned long x_; \ + x_ = udiv128((n0), (n1), 0, (d)); \ + (q) = __real__ x_; \ + (r) = __imag__ x_; \ } while (0) #define UDIV_NEEDS_NORMALIZATION 1 -#define abort() \ - return 0 +#define abort() goto bad_insn #ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN -1 @@ -609,13 +581,7 @@ #define EFLAG_DENORM IEEE_TRAP_ENABLE_DNO #define EFLAG_MASK IEEE_TRAP_ENABLE_MASK -#ifdef FP_TEST_XXX -#define _FP_DENORM_TO_ZERO \ - (tss_flags&IEEE_MAP_DMZ) -#else -#define _FP_DENORM_TO_ZERO \ - ((current->tss.flags)&IEEE_MAP_DMZ) -#endif +#define _FP_DENORM_TO_ZERO ((current->tss.flags) & IEEE_MAP_DMZ) /* Comparison operations */ #define CMPTXX_EQ 0 diff -u --recursive --new-file v2.2.11/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.2.11/linux/arch/i386/defconfig Mon Aug 9 16:05:54 1999 +++ linux/arch/i386/defconfig Wed Aug 25 17:29:46 1999 @@ -230,6 +230,7 @@ # CONFIG_TR is not set # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set +# CONFIG_SEALEVEL_4021 is not set # CONFIG_RCPCI is not set # CONFIG_WAN_DRIVERS is not set # CONFIG_LAPBETHER is not set @@ -239,6 +240,11 @@ # Amateur Radio support # # CONFIG_HAMRADIO is not set + +# +# IrDA subsystem support +# +# CONFIG_IRDA is not set # # ISDN subsystem diff -u --recursive --new-file v2.2.11/linux/arch/i386/kernel/init_task.c linux/arch/i386/kernel/init_task.c --- v2.2.11/linux/arch/i386/kernel/init_task.c Sun Sep 13 12:16:22 1998 +++ linux/arch/i386/kernel/init_task.c Wed Aug 25 17:29:46 1999 @@ -7,7 +7,6 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff -u --recursive --new-file v2.2.11/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c --- v2.2.11/linux/arch/i386/kernel/mtrr.c Fri May 14 09:00:17 1999 +++ linux/arch/i386/kernel/mtrr.c Wed Aug 25 17:29:46 1999 @@ -201,7 +201,17 @@ 19990512 Richard Gooch Minor cleanups. v1.35 + 19990812 Zoltan Boszormenyi + PRELIMINARY CHANGES!!! ONLY FOR TESTING!!! + Rearrange switch() statements so the driver accomodates to + the fact that the AMD Athlon handles its MTRRs the same way + as Intel does. + + 19990819 Alan Cox + Tested Zoltan's changes on a pre production Athlon - 100% + success. Fixed one fall through check to be Intel only. */ + #include #include #include @@ -237,7 +247,7 @@ #include #include "irq.h" -#define MTRR_VERSION "1.35 (19990512)" +#define MTRR_VERSION "1.35a (19990819)" #define TRUE 1 #define FALSE 0 @@ -321,6 +331,8 @@ switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: + if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */ + /* else fall through */ case X86_VENDOR_CENTAUR: return; /*break;*/ @@ -344,6 +356,7 @@ switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: case X86_VENDOR_INTEL: /* Disable MTRRs, and set the default type to uncached */ rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); @@ -365,6 +378,8 @@ switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: + if (boot_cpu_data.x86 >= 6) break; /* Athlon and post-Athlon CPUs */ + /* else fall through */ case X86_VENDOR_CENTAUR: __restore_flags (ctxt->flags); return; @@ -376,6 +391,7 @@ /* Restore MTRRdefType */ switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: case X86_VENDOR_INTEL: wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); break; @@ -406,6 +422,9 @@ switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 < 6) return 2; /* pre-Athlon CPUs */ + /* else fall through */ case X86_VENDOR_INTEL: rdmsr (MTRRcap_MSR, config, dummy); return (config & 0xff); @@ -416,9 +435,6 @@ /* and Centaur has 8 MCR's */ return 8; /*break;*/ - case X86_VENDOR_AMD: - return 2; - /*break;*/ } return 0; } /* End Function get_num_var_ranges */ @@ -430,12 +446,14 @@ switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 < 6) return 1; /* pre-Athlon CPUs */ + /* else fall through */ case X86_VENDOR_INTEL: rdmsr (MTRRcap_MSR, config, dummy); return (config & (1<<10)); /*break;*/ case X86_VENDOR_CYRIX: - case X86_VENDOR_AMD: case X86_VENDOR_CENTAUR: return 1; /*break;*/ @@ -1062,9 +1080,23 @@ if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV; switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 < 6) { /* pre-Athlon CPUs */ + /* Apply the K6 block alignment and size rules + In order + o Uncached or gathering only + o 128K or bigger block + o Power of 2 block + o base suitably aligned to the power + */ + if (type > MTRR_TYPE_WRCOMB || size < (1 << 17) || + (size & ~(size-1))-size || (base & (size-1))) + return -EINVAL; + break; + } /* else fall through */ case X86_VENDOR_INTEL: /* For Intel PPro stepping <= 7, must be 4 MiB aligned */ - if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) && + if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) && (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) - 1 ) ) ) { printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base); @@ -1105,18 +1137,6 @@ return -EINVAL; } break; - case X86_VENDOR_AMD: - /* Apply the K6 block alignment and size rules - In order - o Uncached or gathering only - o 128K or bigger block - o Power of 2 block - o base suitably aligned to the power - */ - if (type > MTRR_TYPE_WRCOMB || size < (1 << 17) || - (size & ~(size-1))-size || (base & (size-1))) - return -EINVAL; - break; default: return -EINVAL; /*break;*/ @@ -1657,6 +1677,12 @@ printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION); switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 < 6) { /* pre-Athlon CPUs */ + get_mtrr = amd_get_mtrr; + set_mtrr_up = amd_set_mtrr_up; + break; + } /* else fall through */ case X86_VENDOR_INTEL: get_mtrr = intel_get_mtrr; set_mtrr_up = intel_set_mtrr_up; @@ -1666,10 +1692,6 @@ set_mtrr_up = cyrix_set_arr_up; get_free_region = cyrix_get_free_region; break; - case X86_VENDOR_AMD: - get_mtrr = amd_get_mtrr; - set_mtrr_up = amd_set_mtrr_up; - break; case X86_VENDOR_CENTAUR: get_mtrr = centaur_get_mcr; set_mtrr_up = centaur_set_mcr_up; @@ -1688,6 +1710,8 @@ mtrr_setup (); switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 < 6) break; /* pre-Athlon CPUs */ case X86_VENDOR_INTEL: get_mtrr_state (&smp_mtrr_state); break; @@ -1724,6 +1748,9 @@ if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return; switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + /* Just for robustness: pre-Athlon CPUs cannot do SMP. */ + if (boot_cpu_data.x86 < 6) break; case X86_VENDOR_INTEL: intel_mtrr_init_secondary_cpu (); break; @@ -1749,6 +1776,8 @@ # ifdef __SMP__ switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 < 6) break; /* pre-Athlon CPUs */ case X86_VENDOR_INTEL: finalize_mtrr_state (&smp_mtrr_state); mtrr_state_warn (smp_changes_mask); diff -u --recursive --new-file v2.2.11/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.2.11/linux/arch/i386/kernel/setup.c Tue Jun 8 10:43:21 1999 +++ linux/arch/i386/kernel/setup.c Wed Aug 25 17:29:46 1999 @@ -14,6 +14,9 @@ * Bart Hartgers , May 199. * * Intel Mobile Pentium II detection fix. Sean Gilley, June 1999. + * + * IDT Winchip tweaks, misc clean ups. + * Dave Jones , August 1999 */ /* @@ -480,6 +483,19 @@ break; } break; + case 6: /* An Athlon. We can trust the BIOS probably */ + { + + u32 ecx, edx, dummy; + cpuid(0x80000005, &dummy, &dummy, &ecx, &edx); + printk("L1 I Cache: %dK L1 D Cache: %dK\n", + ecx>>24, edx>>24); + cpuid(0x80000006, &dummy, &dummy, &ecx, &edx); + printk("L2 Cache: %dK\n", ecx>>16); + c->x86_cache_size = ecx>>16; + break; + } + } return r; } @@ -690,8 +706,9 @@ NULL, NULL, NULL, NULL }}, { X86_VENDOR_INTEL, 6, { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", - NULL, "Pentium II (Deschutes)", "Mobile Pentium II", NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + NULL, "Pentium II (Deschutes)", "Mobile Pentium II", + "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL, + NULL, NULL, NULL, NULL }}, { X86_VENDOR_AMD, 4, { NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", @@ -701,6 +718,11 @@ "K5", "K5", NULL, NULL, "K6", "K6", "K6-2", "K6-3", NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_AMD, 6, + { "Athlon", "Athlon", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, 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 }}, @@ -810,7 +832,6 @@ } } } - } if (p) { @@ -860,22 +881,25 @@ printk("%s", c->x86_model_id); if (c->x86_mask || c->cpuid_level>=0) - printk(" stepping %02x", c->x86_mask); + printk(" stepping %02x\n", c->x86_mask); - if(c->x86_vendor == X86_VENDOR_CENTAUR) - { + if(c->x86_vendor == X86_VENDOR_CENTAUR) { u32 hv,lv; rdmsr(0x107, lv, hv); - printk("\nCentaur FSR was 0x%X ",lv); - lv|=(1<<8); - lv|=(1<<7); + printk("Centaur FSR was 0x%X ",lv); + lv|=(1<<1 | 1<<2 | 1<<7); /* lv|=(1<<6); - may help too if the board can cope */ - printk("now 0x%X", lv); + printk("now 0x%X\n", lv); wrmsr(0x107, lv, hv); /* Emulate MTRRs using Centaur's MCR. */ - c->x86_capability |= X86_FEATURE_MTRR; + c->x86_capability |= X86_FEATURE_MTRR; + + /* Set 3DNow! on Winchip 2 and above. */ + if (c->x86_model >=8) + c->x86_capability |= X86_FEATURE_AMD3D; + + c->x86_capability |=X86_FEATURE_CX8; } - printk("\n"); } /* @@ -910,7 +934,7 @@ c->x86 + '0', c->x86_model, c->x86_model_id[0] ? c->x86_model_id : "unknown"); - + if (c->x86_mask || c->cpuid_level >= 0) p += sprintf(p, "stepping\t: %d\n", c->x86_mask); else @@ -926,14 +950,20 @@ p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size); /* Modify the capabilities according to chip type */ - if (c->x86_vendor == X86_VENDOR_CYRIX) { + switch (c->x86_vendor) { + + case X86_VENDOR_CYRIX: x86_cap_flags[24] = "cxmmx"; - } else if (c->x86_vendor == X86_VENDOR_AMD) { - x86_cap_flags[16] = "fcmov"; - x86_cap_flags[31] = "3dnow"; + break; + + case X86_VENDOR_AMD: if (c->x86 == 5 && c->x86_model == 6) x86_cap_flags[10] = "sep"; - } else if (c->x86_vendor == X86_VENDOR_INTEL) { + x86_cap_flags[16] = "fcmov"; + x86_cap_flags[31] = "3dnow"; + break; + + case X86_VENDOR_INTEL: x86_cap_flags[6] = "pae"; x86_cap_flags[9] = "apic"; x86_cap_flags[14] = "mca"; @@ -941,6 +971,16 @@ x86_cap_flags[17] = "pse36"; x86_cap_flags[18] = "psn"; x86_cap_flags[24] = "osfxsr"; + break; + + case X86_VENDOR_CENTAUR: + if (c->x86_model >=8) /* Only Winchip2 and above */ + x86_cap_flags[31] = "3dnow"; + break; + + default: + /* Unknown CPU manufacturer. Transmeta ? :-) */ + break; } sep_bug = c->x86_vendor == X86_VENDOR_INTEL && diff -u --recursive --new-file v2.2.11/linux/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c --- v2.2.11/linux/arch/m68k/kernel/process.c Tue May 11 09:57:14 1999 +++ linux/arch/m68k/kernel/process.c Wed Aug 25 17:29:46 1999 @@ -40,7 +40,6 @@ */ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; @@ -87,9 +86,6 @@ void machine_power_off(void) { -#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) - apm_set_power_state(APM_STATE_OFF); -#endif } void show_regs(struct pt_regs * regs) diff -u --recursive --new-file v2.2.11/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile --- v2.2.11/linux/arch/ppc/boot/Makefile Fri Jun 4 13:30:47 1999 +++ linux/arch/ppc/boot/Makefile Wed Aug 25 17:29:46 1999 @@ -46,6 +46,10 @@ OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc +ifeq ($(CONFIG_SMP),y) +CFLAGS += -D__SMP__ +endif + OBJECTS += vreset.o kbd.o of1275.o ifeq ($(CONFIG_SERIAL_CONSOLE),y) OBJECTS += ns16550.o diff -u --recursive --new-file v2.2.11/linux/arch/ppc/boot/misc.c linux/arch/ppc/boot/misc.c --- v2.2.11/linux/arch/ppc/boot/misc.c Fri Jun 4 13:30:47 1999 +++ linux/arch/ppc/boot/misc.c Wed Aug 25 17:29:46 1999 @@ -1,7 +1,7 @@ /* * misc.c * - * $Id: misc.c,v 1.64.2.2 1999/05/29 19:09:29 cort Exp $ + * $Id: misc.c,v 1.64.2.4 1999/07/22 03:28:03 cort Exp $ * * Adapted for PowerPC by Gary Thomas * @@ -311,6 +311,33 @@ unsigned char sanity[0x2000]; +/* + * This routine is used to control the second processor on the + * Motorola dual processor platforms. + */ +void +park_cpus() +{ +#ifdef __SMP__ + volatile void (*go)(RESIDUAL *, int, int, char *, int); + unsigned int i; + volatile unsigned long *smp_iar = &(hold_residual->VitalProductData.SmpIar); + + /* Wait for indication to continue. If the kernel + was not compiled with SMP support then the second + processor will spin forever here makeing the kernel + multiprocessor safe. */ + while (*smp_iar == 0) { + for (i=0; i < 512; i++); + } + + (unsigned long)go = hold_residual->VitalProductData.SmpIar; + go(hold_residual, 0, 0, cmd_line, sizeof(cmd_preset)); +#else + while(1); +#endif +} + unsigned long decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual, void *OFW_interface) @@ -328,6 +355,7 @@ int res, size; unsigned char board_type; unsigned char base_mod; + int start_multi = 0; lines = 25; cols = 80; @@ -368,6 +396,14 @@ keyb_present = 0; /* no keyboard */ } } + + /* If this is a multiprocessor system then + * park the other processor so that the + * kernel knows where to find them. + */ + if (residual->MaxNumCpus > 1) { + start_multi = 1; + } } memcpy(hold_residual,residual,sizeof(RESIDUAL)); } else { @@ -408,7 +444,15 @@ residual = hold_residual; /* Turn MMU back off */ _put_MSR(orig_MSR & ~0x0030); - } + } + + if (start_multi) { + hold_residual->VitalProductData.SmpIar = 0; + hold_residual->Cpus[1].CpuState = CPU_GOOD_FW; + residual->VitalProductData.SmpIar = (unsigned long)park_cpus; + residual->Cpus[1].CpuState = CPU_GOOD; + hold_residual->VitalProductData.Reserved5 = 0xdeadbeef; + } /* assume the chunk below 8M is free */ end_avail = (char *)0x00800000; @@ -465,22 +509,23 @@ puts(" "); puthex((unsigned long)zimage_size+(unsigned long)zimage_start); puts("\n"); - } - /* relocate initrd */ - if ( initrd_start ) - { + /* relocate initrd */ + if ( initrd_start ) + { + puts("initrd at: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + avail_ram = (char *)PAGE_ALIGN( + (unsigned long)zimage_size+(unsigned long)zimage_start); + memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); + initrd_start = (unsigned long)avail_ram; + initrd_end = initrd_start + INITRD_SIZE; + puts("relocated to: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + } + } else if ( initrd_start ) { puts("initrd at: "); puthex(initrd_start); puts(" "); puthex(initrd_end); puts("\n"); -#ifdef OMIT - avail_ram = (char *)PAGE_ALIGN( - (unsigned long)zimage_size+(unsigned long)zimage_start); - memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE ); - initrd_start = (unsigned long)avail_ram; - initrd_end = initrd_start + INITRD_SIZE; - puts("relocated to: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); -#endif } avail_ram = (char *)0x00400000; @@ -528,6 +573,7 @@ gunzip(0, 0x400000, zimage_start, &zimage_size); puts("done.\n"); + puts("Now booting the kernel\n"); return (unsigned long)hold_residual; } diff -u --recursive --new-file v2.2.11/linux/arch/ppc/chrpboot/mknote.c linux/arch/ppc/chrpboot/mknote.c --- v2.2.11/linux/arch/ppc/chrpboot/mknote.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/chrpboot/mknote.c Wed Aug 25 17:29:46 1999 @@ -0,0 +1,43 @@ +/* + * Copyright (C) Cort Dougan 1999. + * + * 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. + * + * Generate a note section as per the CHRP specification. + * + */ + +#include + +#define PL(x) printf("%c%c%c%c", ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, (x)&0xff ); + +int main(void) +{ +/* header */ + /* namesz */ + PL(strlen("PowerPC")+1); + /* descrsz */ + PL(6*4); + /* type */ + PL(0x1275); + /* name */ + printf("PowerPC"); printf("%c", 0); + +/* descriptor */ + /* real-mode */ + PL(0xffffffff); + /* real-base */ + PL(0x00c00000); + /* real-size */ + PL(0xffffffff); + /* virt-base */ + PL(0xffffffff); + /* virt-size */ + PL(0xffffffff); + /* load-base */ + PL(0x4000); + return 0; +} diff -u --recursive --new-file v2.2.11/linux/arch/ppc/defconfig linux/arch/ppc/defconfig --- v2.2.11/linux/arch/ppc/defconfig Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/defconfig Wed Aug 25 17:29:46 1999 @@ -9,14 +9,13 @@ CONFIG_6xx=y # CONFIG_PPC64 is not set # CONFIG_8xx is not set -CONFIG_PMAC=y +# CONFIG_PMAC is not set # CONFIG_PREP is not set # CONFIG_CHRP is not set -# CONFIG_ALL_PPC is not set +CONFIG_ALL_PPC=y # CONFIG_APUS is not set # CONFIG_MBX is not set # CONFIG_SMP is not set -CONFIG_MACH_SPECIFIC=y CONFIG_6xx=y # @@ -24,7 +23,7 @@ # CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y -# CONFIG_MODVERSIONS is not set +CONFIG_MODVERSIONS=y CONFIG_KMOD=y CONFIG_PCI=y # CONFIG_PCI_QUIRKS is not set @@ -35,11 +34,10 @@ # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y -CONFIG_BINFMT_MISC=m +# CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set -CONFIG_PARPORT=m -# CONFIG_PARPORT_PC is not set -# CONFIG_VGA_CONSOLE is not set +# CONFIG_PARPORT is not set +CONFIG_VGA_CONSOLE=y CONFIG_FB=y CONFIG_FB_COMPAT_XPMAC=y CONFIG_PMAC_PBOOK=y @@ -52,6 +50,7 @@ # CONFIG_TOTALMP is not set CONFIG_BOOTX_TEXT=y # CONFIG_MOTOROLA_HOTSWAP is not set +# CONFIG_CMDLINE_BOOL is not set # # Plug and Play support @@ -61,7 +60,7 @@ # # Block devices # -# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_IDE=y # @@ -75,16 +74,8 @@ # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_BLK_DEV_CMD640 is not set # CONFIG_BLK_DEV_RZ1000 is not set -CONFIG_BLK_DEV_IDEPCI=y -CONFIG_BLK_DEV_IDEDMA=y -# CONFIG_BLK_DEV_OFFBOARD is not set -CONFIG_IDEDMA_AUTO=y -# CONFIG_BLK_DEV_OPTI621 is not set -# CONFIG_BLK_DEV_TRM290 is not set -# CONFIG_BLK_DEV_NS87415 is not set -# CONFIG_BLK_DEV_VIA82C586 is not set -CONFIG_BLK_DEV_CMD646=y -# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_BLK_DEV_IDEPCI is not set +CONFIG_BLK_DEV_SL82C105=y CONFIG_BLK_DEV_IDE_PMAC=y CONFIG_BLK_DEV_IDEDMA_PMAC=y CONFIG_BLK_DEV_IDEDMA=y @@ -100,10 +91,8 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_DAC960 is not set -CONFIG_PARIDE_PARPORT=m +CONFIG_PARIDE_PARPORT=y # CONFIG_PARIDE is not set -# CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_DEV_HD is not set # @@ -125,7 +114,7 @@ # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=y -# CONFIG_SYN_COOKIES is not set +CONFIG_SYN_COOKIES=y # # (it is safe to leave these untouched) @@ -200,14 +189,14 @@ # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_G_NCR5380_PORT is not set +# CONFIG_SCSI_G_NCR5380_MEM is not set # CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set -# CONFIG_SCSI_PPA is not set -# CONFIG_SCSI_IMM is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_SYM53C416 is not set # CONFIG_SCSI_NCR53C7xx is not set -CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX is not set CONFIG_SCSI_SYM53C8XX=y CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 @@ -215,7 +204,7 @@ # CONFIG_SCSI_NCR53C8XX_PROFILE is not set # CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set # CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT=y +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set @@ -237,18 +226,10 @@ # Network device support # CONFIG_NETDEVICES=y - -# -# ARCnet devices -# # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set # CONFIG_ETHERTAP is not set - -# -# Ethernet (10 or 100Mbit) -# CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_BMAC=y @@ -258,10 +239,10 @@ # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set # CONFIG_YELLOWFIN is not set +# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y -# CONFIG_PCNET32 is not set -# CONFIG_ACENIC is not set +CONFIG_PCNET32=y # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set @@ -280,36 +261,22 @@ # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_HIPPI is not set - -# -# Appletalk devices -# +# CONFIG_DLCI is not set # CONFIG_LTPC is not set # CONFIG_COPS is not set # CONFIG_IPDDP is not set -# CONFIG_PLIP is not set -CONFIG_PPP=m +CONFIG_PPP=y # # CCP compressors for PPP are only built as modules. # # CONFIG_SLIP is not set # CONFIG_NET_RADIO is not set - -# -# Token ring devices -# # CONFIG_TR is not set -# CONFIG_RCPCI is not set # CONFIG_SHAPER is not set - -# -# Wan interfaces -# # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set -# CONFIG_SEALEVEL_4021 is not set -# CONFIG_DLCI is not set +# CONFIG_RCPCI is not set # # Amateur Radio support @@ -335,12 +302,16 @@ CONFIG_FB_CONTROL=y CONFIG_FB_PLATINUM=y CONFIG_FB_VALKYRIE=y -CONFIG_FB_ATY=y +# CONFIG_FB_ATY is not set CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set -# CONFIG_FB_MATROX is not set -CONFIG_FB_ATY=y +CONFIG_FB_MATROX=y +# CONFIG_FB_MATROX_MILLENIUM is not set +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G100=y +# CONFIG_FB_MATROX_MULTIHEAD is not set +# CONFIG_FB_ATY is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -362,13 +333,22 @@ # CONFIG_VT=y CONFIG_VT_CONSOLE=y -# CONFIG_SERIAL is not set +CONFIG_SERIAL=m # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 -# CONFIG_PRINTER is not set -# CONFIG_MOUSE is not set +CONFIG_MOUSE=y + +# +# Mice +# +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set CONFIG_NVRAM=y @@ -389,6 +369,14 @@ # Ftape, the floppy tape device driver # # CONFIG_FTAPE is not set +# CONFIG_FT_NORMAL_DEBUG is not set +# CONFIG_FT_FULL_DEBUG is not set +# CONFIG_FT_NO_TRACE is not set +# CONFIG_FT_NO_TRACE_AT_ALL is not set +# CONFIG_FT_STD_FDC is not set +# CONFIG_FT_MACH2 is not set +# CONFIG_FT_PROBE_FC10 is not set +# CONFIG_FT_ALT_FDC is not set # # USB drivers - not for the faint of heart @@ -403,10 +391,10 @@ # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set CONFIG_HFS_FS=y -CONFIG_FAT_FS=m -CONFIG_MSDOS_FS=m +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y # CONFIG_UMSDOS_FS is not set -CONFIG_VFAT_FS=m +CONFIG_VFAT_FS=y CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set @@ -419,7 +407,6 @@ CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set -# CONFIG_EFS_FS is not set # # Network File Systems @@ -471,7 +458,7 @@ # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set -CONFIG_NLS_ISO8859_15=y +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # @@ -484,7 +471,34 @@ # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set -# CONFIG_SOUND_OSS is not set +CONFIG_SOUND_OSS=y +# CONFIG_SOUND_DMAP is not set +# CONFIG_SOUND_PAS is not set +# CONFIG_SOUND_SB is not set +# CONFIG_SOUND_ADLIB is not set +# CONFIG_SOUND_GUS is not set +# CONFIG_SOUND_MPU401 is not set +# CONFIG_SOUND_PSS is not set +# CONFIG_SOUND_MSS is not set +# CONFIG_SOUND_SSCAPE is not set +# CONFIG_SOUND_TRIX is not set +# CONFIG_SOUND_MAD16 is not set +# CONFIG_SOUND_WAVEFRONT is not set +CONFIG_SOUND_CS4232=m +# CONFIG_SOUND_OPL3SA2 is not set +# CONFIG_SOUND_MAUI is not set +# CONFIG_SOUND_SGALAXY is not set +# CONFIG_SOUND_AD1816 is not set +# CONFIG_SOUND_OPL3SA1 is not set +# CONFIG_SOUND_SOFTOSS is not set +# CONFIG_SOUND_YM3812 is not set +# CONFIG_SOUND_VMIDI is not set +# CONFIG_SOUND_UART6850 is not set + +# +# Additional low level sound drivers +# +# CONFIG_LOWLEVEL_SOUND is not set # # Kernel hacking diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile --- v2.2.11/linux/arch/ppc/kernel/Makefile Fri Jun 4 13:30:47 1999 +++ linux/arch/ppc/kernel/Makefile Wed Aug 25 17:29:46 1999 @@ -25,6 +25,9 @@ ifdef CONFIG_TOTALMP O_OBJS += totalmp.o endif +ifdef CONFIG_PMAC_PBOOK +O_OBJS += sleep.o +endif ifeq ($(CONFIG_MBX),y) O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o i8259.o ppc8xx_pic.o diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c --- v2.2.11/linux/arch/ppc/kernel/chrp_pci.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/chrp_pci.c Wed Aug 25 17:29:46 1999 @@ -300,11 +300,11 @@ } /* the F50 identifies the amd as a trident */ if ( (dev->vendor == PCI_VENDOR_ID_TRIDENT) && - (dev->class == PCI_CLASS_NETWORK_ETHERNET) ) + (dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET) ) { dev->vendor = PCI_VENDOR_ID_AMD; - pcibios_write_config_word(dev->bus->number, dev->devfn, - PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); + pcibios_write_config_word(dev->bus->number, + dev->devfn, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); } } } diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c --- v2.2.11/linux/arch/ppc/kernel/chrp_setup.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/chrp_setup.c Wed Aug 25 17:29:46 1999 @@ -294,16 +294,19 @@ { struct property *p; device = find_devices("rtas"); - for ( p = device->properties; - p && strncmp(p->name, "rtas-event-scan-rate", 20); - p = p->next ) - /* nothing */ ; - if ( p && *(unsigned long *)p->value ) + if ( device ) { - rtas_event_scan_rate = (HZ/(*(unsigned long *)p->value)*30)-1; - rtas_event_scan_ct = 1; - printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", - *(unsigned long *)p->value, rtas_event_scan_rate ); + for ( p = device->properties; + p && strncmp(p->name, "rtas-event-scan-rate", 20); + p = p->next ) + /* nothing */ ; + if ( p && *(unsigned long *)p->value ) + { + rtas_event_scan_rate = (HZ/(*(unsigned long *)p->value)*30)-1; + rtas_event_scan_ct = 1; + printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", + *(unsigned long *)p->value, rtas_event_scan_rate ); + } } } } @@ -312,11 +315,10 @@ chrp_event_scan(void) { unsigned char log[1024]; + unsigned long ret = 0; if ( rtas_event_scan_rate && (rtas_event_scan_ct-- <= 0) ) - { - call_rtas( "event-scan", 4, 1, NULL, 0x0, 1, __pa(log), 1024 ); - rtas_event_scan_ct = rtas_event_scan_rate; - } + call_rtas( "event-scan", 4, 1, &ret, 0xffffffff, 0, + __pa(log), 1024 ); } void diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/feature.c linux/arch/ppc/kernel/feature.c --- v2.2.11/linux/arch/ppc/kernel/feature.c Wed Mar 10 21:30:31 1999 +++ linux/arch/ppc/kernel/feature.c Wed Aug 25 17:29:46 1999 @@ -41,7 +41,9 @@ OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ 0, /* FEATURE_BMac_reset */ 0, /* FEATURE_BMac_IO_enable */ - 0 /* FEATURE_Modem_Reset -> guess...*/ + 0, /* FEATURE_Modem_Reset -> guess... */ + OH_IDE_POWER, /* FEATURE_IDE_DiskPower -> guess... */ + OH_IDE_RESET /* FEATURE_IDE_Reset (0 based) -> guess... */ }; /* assume these are the same as the ohare until proven otherwise */ @@ -63,7 +65,9 @@ OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ 0x80000000, /* FEATURE_BMac_reset */ 0x60000000, /* FEATURE_BMac_IO_enable */ - 0x02000000 /* FEATURE_Modem_Reset -> guess...*/ + 0x02000000, /* FEATURE_Modem_Reset -> guess...*/ + OH_IDE_POWER, /* FEATURE_IDE_DiskPower -> guess... */ + OH_IDE_RESET /* FEATURE_IDE_Reset (0 based) -> guess... */ }; /* definition of a feature controller object */ @@ -191,8 +195,8 @@ save_flags(flags); cli(); - st_le32( controllers[controller].reg, - ld_le32(controllers[controller].reg) | + out_le32( controllers[controller].reg, + in_le32(controllers[controller].reg) | controllers[controller].bits[f]); restore_flags(flags); udelay(10); @@ -220,8 +224,8 @@ save_flags(flags); cli(); - st_le32( controllers[controller].reg, - ld_le32(controllers[controller].reg) & + out_le32( controllers[controller].reg, + in_le32(controllers[controller].reg) & ~(controllers[controller].bits[f])); restore_flags(flags); udelay(10); @@ -241,7 +245,7 @@ if (controller < 0) return controller; - return (ld_le32(controllers[controller].reg) & + return (in_le32(controllers[controller].reg) & controllers[controller].bits[f]) != 0; } diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S --- v2.2.11/linux/arch/ppc/kernel/head.S Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/head.S Wed Aug 25 17:29:46 1999 @@ -1,7 +1,7 @@ /* * arch/ppc/kernel/head.S * - * $Id: head.S,v 1.130.2.2 1999/06/30 04:53:21 paulus Exp $ + * $Id: head.S,v 1.130.2.3 1999/08/10 21:36:48 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -433,16 +433,7 @@ * ready to work. */ #endif /* CONFIG_8xx */ - -turn_on_mmu: - mfmsr r0 - ori r0,r0,MSR_DR|MSR_IR - mtspr SRR1,r0 - lis r0,start_here@h - ori r0,r0,start_here@l - mtspr SRR0,r0 - SYNC - rfi /* enables MMU */ + b turn_on_mmu /* * GCC sometimes accesses words at negative offsets from the stack @@ -1729,6 +1720,37 @@ . = 0x4000 #endif +turn_on_mmu: + mfmsr r0 + ori r1,r0,MSR_DR|MSR_IR + mtspr SRR1,r1 +#ifdef CONFIG_SMP + /* see the function start_here_ibm_hack for explanation -- Cort */ + andi. 0,r1,MSR_DR /* check if the MMU is already on */ + bne 10f + lis r5,smp_ibm_chrp_hack@h + ori r5,r5,smp_ibm_chrp_hack@l + tophys(r5,r5,r6) + lwz r5,0(r5) + cmpi 0,r5,0 + beq 10f + lis r5,first_cpu_booted@h + ori r5,r5,first_cpu_booted@l + tophys(r5,r5,r6) + lwz r5,0(r5) + cmpi 0,r5,0 + beq 10f + lis r0,start_here_ibm_hack@h + ori r0,r0,start_here_ibm_hack@l + b 1010f +10: +#endif /* CONFIG_SMP */ + lis r0,start_here@h + ori r0,r0,start_here@l +1010: mtspr SRR0,r0 + SYNC + rfi /* enables MMU */ + #ifdef CONFIG_SMP .globl __secondary_start_psurge __secondary_start_psurge: @@ -1758,11 +1780,27 @@ blr #endif /* CONFIG_SMP */ -/* - * This is where the main kernel code starts. - */ -start_here: +/* + * We get _strange_ behavior on the new IBM chrp firmware. + * We end up here with the MMU disabled, even though we enable + * it with the rfi that takes us here. Somehow, a physical address + * access fixes this by enabling the MMU. + * + * The IBM engineers can't explain this behavior and AIX doesn't + * seem to find it. This hack gets around it for now. + * -- Cort + */ +start_here_ibm_hack: + mfmsr r1 + lis r5,smp_ibm_chrp_hack@h + ori r5,r5,smp_ibm_chrp_hack@l + tophys(r5,r5,r6) + mfmsr r1 + stw r1,_MSR-16(r5) + addi r5,r5,_MSR-16 + dcbf 0,r5 #ifndef CONFIG_8xx +start_here: /* * Enable caches and 604-specific features if necessary. */ diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/i8259.c linux/arch/ppc/kernel/i8259.c --- v2.2.11/linux/arch/ppc/kernel/i8259.c Tue May 11 08:24:32 1999 +++ linux/arch/ppc/kernel/i8259.c Wed Aug 25 17:29:46 1999 @@ -13,33 +13,32 @@ int i8259_irq(int cpu) { int irq; + unsigned char irr; /* * Perform an interrupt acknowledge cycle on controller 1 - */ - outb(0x0C, 0x20); - irq = inb(0x20) & 7; - if (irq == 2) - { - /* + */ + irr = inb(0x20) & ~cached_21; + if (!irr) return -1; + irq = 0; + while ((irq < 7) && !(irr&0x01)) + { + irq++; + irr >>= 1; + } + if (irq == 2) + { + /* * Interrupt is cascaded so perform interrupt * acknowledge on controller 2 */ - outb(0x0C, 0xA0); - irq = (inb(0xA0) & 7) + 8; - } - else if (irq==7) - { - /* - * This may be a spurious interrupt - * - * Read the interrupt status register. If the most - * significant bit is not set then there is no valid - * interrupt - */ - outb(0x0b, 0x20); - if(~inb(0x20)&0x80) - return -1; + irr = inb(0xA0) & ~cached_A1; + irq = 8; + while ((irq < 15) && !(irr&0x01)) + { + irq++; + irr >>= 1; + } } return irq; } @@ -53,13 +52,13 @@ cached_A1 |= 1 << (irq_nr-8); inb(0xA1); /* DUMMY */ outb(cached_A1,0xA1); - outb(0x20,0xA0); /* Non-specific EOI */ - outb(0x20,0x20); /* Non-specific EOI to cascade */ + outb(0x62,0x20); /* Specific EOI to cascade */ + outb(0x60|(irq_nr-8),0xA0); /* Specific EOI */ } else { cached_21 |= 1 << irq_nr; inb(0x21); /* DUMMY */ outb(cached_21,0x21); - outb(0x20,0x20); /* Non-specific EOI */ + outb(0x60|irq_nr,0x20); /* Specific EOI */ } } @@ -82,7 +81,6 @@ static void i8259_unmask_irq(unsigned int irq_nr) { - if ( irq_nr >= i8259_pic.irq_offset ) irq_nr -= i8259_pic.irq_offset; if ( irq_nr < 8 ) diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c --- v2.2.11/linux/arch/ppc/kernel/irq.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/irq.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: irq.c,v 1.105.2.2 1999/06/17 01:16:10 paulus Exp $ + * $Id: irq.c,v 1.105.2.3 1999/07/22 01:49:41 cort Exp $ * * arch/ppc/kernel/irq.c * @@ -139,13 +139,16 @@ /* Free */ for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) { - /* Found it - now free it */ - save_flags(flags); - cli(); - *p = action->next; - restore_flags(flags); - irq_kfree(action); - return 0; + if (action->dev_id == dev_id) + { + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + restore_flags(flags); + irq_kfree(action); + return 0; + } } return -ENOENT; } diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S --- v2.2.11/linux/arch/ppc/kernel/misc.S Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/misc.S Wed Aug 25 17:29:46 1999 @@ -17,6 +17,7 @@ #include #include #include +#include #include "ppc_asm.tmpl" #include "ppc_defs.h" @@ -378,6 +379,7 @@ * * ashrdi3: XXXYYY/ZZZAAA -> SSSXXX/YYYZZZ * ashldi3: XXXYYY/ZZZAAA -> YYYZZZ/AAA000 + * lshrdi3: XXXYYY/ZZZAAA -> 000XXX/YYYZZZ */ _GLOBAL(__ashrdi3) li r6,32 @@ -387,7 +389,7 @@ or r4,r4,r7 /* YYYZZZ */ sraw r3,r3,r5 /* SSSXXX */ blr - + _GLOBAL(__ashldi3) li r6,32 sub r6,r6,r5 @@ -397,6 +399,15 @@ or r3,r3,r7 /* YYYZZZ */ blr +_GLOBAL(__lshrdi3) + li r6,32 + sub r6,r6,r5 + slw r7,r3,r6 /* isolate YYY */ + srw r4,r4,r5 /* isolate ZZZ */ + or r4,r4,r7 /* YYYZZZ */ + srw r3,r3,r5 /* 000XXX */ + blr + _GLOBAL(abs) cmpi 0,r3,0 bge 10f @@ -459,36 +470,42 @@ Author: Terry Greeniaus (tgree@phys.ualberta.ca) Please e-mail updates to this file to me, thanks! */ +/* Usage: + + When setting the L2CR register, you must do a few special + things. If you are enabling the cache, you must perform a + global invalidate. If you are disabling the cache, you must + flush the cache contents first. This routine takes care of + doing these things. When first enabling the cache, make sure + you pass in the L2CR you want, as well as passing in the + global invalidate bit set. A global invalidate will only be + performed if the L2I bit is set in applyThis. When enabling + the cache, you should also set the L2E bit in applyThis. If + you want to modify the L2CR contents after the cache has been + enabled, the recommended procedure is to first call + __setL2CR(0) to disable the cache and then call it again with + the new values for L2CR. Examples: + + _setL2CR(0) - disables the cache + _setL2CR(0xB3A04000) - enables my G3 upgrade card: + - L2E set to turn on the cache + - L2SIZ set to 1MB + - L2CLK set to 1:1 + - L2RAM set to pipelined synchronous late-write + - L2I set to perform a global invalidation + - L2OH set to 0.5 nS + - L2DF set because this upgrade card + requires it + + A similar call should work for your card. You need to know + the correct setting for your card and then place them in the + fields I have outlined above. Other fields support optional + features, such as L2DO which caches only data, or L2TS which + causes cache pushes from the L1 cache to go to the L2 cache + instead of to main memory. +*/ _GLOBAL(_set_L2CR) - /* Usage: - - When setting the L2CR register, you must do a few special things. If you are enabling the - cache, you must perform a global invalidate. If you are disabling the cache, you must - flush the cache contents first. This routine takes care of doing these things. When first - enabling the cache, make sure you pass in the L2CR you want, as well as passing in the - global invalidate bit set. A global invalidate will only be performed if the L2I bit is set - in applyThis. When enabling the cache, you should also set the L2E bit in applyThis. If you - want to modify the L2CR contents after the cache has been enabled, the recommended - procedure is to first call __setL2CR(0) to disable the cache and then call it again with - the new values for L2CR. Examples: - - _setL2CR(0) - disables the cache - _setL2CR(0xB3A04000) - enables my G3 upgrade card: - - L2E set to turn on the cache - - L2SIZ set to 1MB - - L2CLK set to 1:1 - - L2RAM set to pipelined syncronous late-write - - L2I set to perform a global invalidation - - L2OH set to 0.5 nS - - L2DF set because this upgrade card requires it - - A similar call should work for your card. You need to know the correct setting for your - card and then place them in the fields I have outlined above. Other fields support optional - features, such as L2DO which caches only data, or L2TS which causes cache pushes from - the L1 cache to go to the L2 cache instead of to main memory. - */ - /* Make sure this is a 750 chip */ mfspr r4,PVR rlwinm r4,r4,16,16,31 @@ -500,76 +517,74 @@ thisIs750: /* Get the current enable bit of the L2CR into r4 */ mfspr r4,L2CR - rlwinm r4,r4,0,0,0 + mfmsr r7 /* See if we want to perform a global inval this time. */ - rlwinm r6,r3,0,10,10 /* r6 contains the new invalidate bit */ - rlwinm. r5,r3,0,0,0 /* r5 contains the new enable bit */ - rlwinm r3,r3,0,11,9 /* Turn off the invalidate bit */ - rlwinm r3,r3,0,1,31 /* Turn off the enable bit */ - or r3,r3,r4 /* Keep the enable bit the same as it was for now. */ - bne dontDisableCache /* Only disable the cache if L2CRApply has the enable bit off */ + rlwinm r6,r3,0,10,10 /* r6 contains the new invalidate bit */ + rlwinm. r5,r3,0,0,0 /* r5 contains the new enable bit */ + rlwinm r3,r3,0,11,9 /* Turn off the invalidate bit */ + rlwimi r3,r4,0,0,0 /* Keep the enable bit the same as it was. */ + bne dontDisableCache /* Only disable the cache if L2CRApply + has the enable bit off */ disableCache: - /* Disable the cache. First, we turn off data relocation. */ - mfmsr r7 - rlwinm r4,r7,0,28,26 /* Turn off DR bit */ - rlwinm r4,r4,0,17,15 /* Turn off EE bit - an external exception while we are flushing - the cache is fatal (comment this line and see!) */ + /* Disable the cache. First, we turn off interrupts. + An interrupt while we are flushing the cache could bring + in data which may not get properly flushed. */ + rlwinm r4,r7,0,17,15 /* Turn off EE bit */ sync mtmsr r4 sync - /* - Now, read the first 2MB of memory to put new data in the cache. - (Actually we only need the size of the L2 cache plus - the size of the L1 cache, but 2MB will cover everything just to be safe). - */ +/* + Now, read the first 2MB of memory to put new data in the cache. + (Actually we only need the size of the L2 cache plus the size + of the L1 cache, but 2MB will cover everything just to be safe). +*/ lis r4,0x0001 mtctr r4 - li r4,0 -loadLoop: - lwzx r0,r0,r4 + lis r4,KERNELBASE@h +1: lwzx r0,r0,r4 addi r4,r4,0x0020 /* Go to start of next cache line */ - bdnz loadLoop + bdnz 1b /* Now, flush the first 2MB of memory */ lis r4,0x0001 mtctr r4 - li r4,0 + lis r4,KERNELBASE@h sync -flushLoop: - dcbf r0,r4 +2: dcbf r0,r4 addi r4,r4,0x0020 /* Go to start of next cache line */ - bdnz flushLoop + bdnz 2b /* Turn off the L2CR enable bit. */ rlwinm r3,r3,0,1,31 - /* Reenable data relocation. */ - sync - mtmsr r7 - sync - dontDisableCache: /* Set up the L2CR configuration bits */ sync mtspr L2CR,r3 sync + + /* Reenable interrupts if necessary. */ + mtmsr r7 + sync + cmplwi r6,0 beq noInval /* Perform a global invalidation */ oris r3,r3,0x0020 sync - mtspr 1017,r3 + mtspr L2CR,r3 sync -invalCompleteLoop: /* Wait for the invalidation to complete */ - mfspr r3,1017 + + /* Wait for the invalidation to complete */ +3: mfspr r3,L2CR rlwinm. r4,r3,0,31,31 - bne invalCompleteLoop + bne 3b - rlwinm r3,r3,0,11,9; /* Turn off the L2I bit */ + rlwinm r3,r3,0,11,9 /* Turn off the L2I bit */ sync mtspr L2CR,r3 sync @@ -578,8 +593,7 @@ /* See if we need to enable the cache */ cmplwi r5,0 beqlr - -enableCache: + /* Enable the cache */ oris r3,r3,0x8000 mtspr L2CR,r3 diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c --- v2.2.11/linux/arch/ppc/kernel/pci.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/pci.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.54 1999/03/18 04:16:04 cort Exp $ + * $Id: pci.c,v 1.54.2.1 1999/07/20 05:04:41 paulus Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/pmac_pic.c linux/arch/ppc/kernel/pmac_pic.c --- v2.2.11/linux/arch/ppc/kernel/pmac_pic.c Wed May 12 08:50:08 1999 +++ linux/arch/ppc/kernel/pmac_pic.c Wed Aug 25 17:29:46 1999 @@ -360,3 +360,41 @@ request_irq(20, xmon_irq, 0, "NMI - XMON", 0); #endif /* CONFIG_XMON */ } + +#ifdef CONFIG_PMAC_PBOOK +/* + * These procedures are used in implementing sleep on the powerbooks. + * sleep_save_intrs() saves the states of all interrupt enables + * and disables all interupts except for the nominated one. + * sleep_restore_intrs() restores the states of all interrupt enables. + */ +unsigned int sleep_save_mask[2]; + +void +sleep_save_intrs(int viaint) +{ + sleep_save_mask[0] = ppc_cached_irq_mask[0]; + sleep_save_mask[1] = ppc_cached_irq_mask[1]; + ppc_cached_irq_mask[0] = 0; + ppc_cached_irq_mask[1] = 0; + set_bit(viaint, ppc_cached_irq_mask); + out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); + if (max_real_irqs > 32) + out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); + mb(); +} + +void +sleep_restore_intrs(void) +{ + int i; + + out_le32(&pmac_irq_hw[0]->enable, 0); + if (max_real_irqs > 32) + out_le32(&pmac_irq_hw[1]->enable, 0); + mb(); + for (i = 0; i < max_real_irqs; ++i) + if (test_bit(i, sleep_save_mask)) + pmac_unmask_irq(i); +} +#endif /* CONFIG_PMAC_PBOOK */ diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c --- v2.2.11/linux/arch/ppc/kernel/pmac_setup.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/pmac_setup.c Wed Aug 25 17:29:46 1999 @@ -161,16 +161,24 @@ /* find ram info */ np = find_devices("memory"); if (np != 0) { + int n; struct reg_property *reg = (struct reg_property *) - get_property(np, "reg", NULL); + get_property(np, "reg", &n); + if (reg != 0) { - len += sprintf(buffer+len, "memory\t\t: %dMB\n", - reg->size >> 20); + unsigned long total = 0; + + for (n /= sizeof(struct reg_property); n > 0; --n) + total += (reg++)->size; + len += sprintf(buffer+len, "memory\t\t: %luMB\n", + total >> 20); } } /* Checks "l2cr-value" property in the registry */ np = find_devices("cpus"); + if (np == 0) + np = find_type_devices("cpu"); if (np != 0) { unsigned int *l2cr = (unsigned int *) get_property(np, "l2cr-value", NULL); @@ -261,6 +269,8 @@ /* Checks "l2cr-value" property in the registry */ if ( (_get_PVR() >> 16) == 8) { struct device_node *np = find_devices("cpus"); + if (np == 0) + np = find_type_devices("cpu"); if (np != 0) { unsigned int *l2cr = (unsigned int *) get_property(np, "l2cr-value", NULL); diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/pmac_time.c linux/arch/ppc/kernel/pmac_time.c --- v2.2.11/linux/arch/ppc/kernel/pmac_time.c Tue Aug 4 23:57:51 1998 +++ linux/arch/ppc/kernel/pmac_time.c Wed Aug 25 17:29:46 1999 @@ -139,13 +139,12 @@ /* * Reset the time after a sleep. */ -static int time_sleep_notify(struct notifier_block *this, unsigned long event, - void *x) +static int time_sleep_notify(struct pmu_sleep_notifier *self, int when) { static unsigned long time_diff; - switch (event) { - case PBOOK_SLEEP: + switch (when) { + case PBOOK_SLEEP_NOW: time_diff = xtime.tv_sec - pmac_get_rtc_time(); break; case PBOOK_WAKE: @@ -155,11 +154,11 @@ last_rtc_update = xtime.tv_sec; break; } - return NOTIFY_DONE; + return PBOOK_SLEEP_OK; } -static struct notifier_block time_sleep_notifier = { - time_sleep_notify, NULL, 100 +static struct pmu_sleep_notifier time_sleep_notifier = { + time_sleep_notify, SLEEP_LEVEL_MISC, }; #endif /* CONFIG_PMAC_PBOOK */ @@ -174,7 +173,7 @@ int freq, *fp, divisor; #ifdef CONFIG_PMAC_PBOOK - notifier_chain_register(&sleep_notifier_list, &time_sleep_notifier); + pmu_register_sleep_notifier(&time_sleep_notifier); #endif /* CONFIG_PMAC_PBOOK */ if (via_calibrate_decr()) diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/ppc-stub.c linux/arch/ppc/kernel/ppc-stub.c --- v2.2.11/linux/arch/ppc/kernel/ppc-stub.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/ppc-stub.c Wed Aug 25 17:29:46 1999 @@ -1,4 +1,4 @@ -/* $Id: ppc-stub.c,v 1.4 1998/07/28 08:25:01 paulus Exp $ +/* $Id: ppc-stub.c,v 1.4.2.1 1999/07/20 05:04:42 paulus Exp $ * ppc-stub.c: KGDB support for the Linux kernel. * * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/ppc_htab.c linux/arch/ppc/kernel/ppc_htab.c --- v2.2.11/linux/arch/ppc/kernel/ppc_htab.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/ppc_htab.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: ppc_htab.c,v 1.26 1998/12/10 00:24:23 cort Exp $ + * $Id: ppc_htab.c,v 1.26.2.2 1999/08/10 01:55:03 paulus Exp $ * * PowerPC hash table management proc entry. Will show information * about the current hash table and will allow changes to it. @@ -532,7 +532,21 @@ int vleft, first=1, len, left, val; #define TMPBUFLEN 256 char buf[TMPBUFLEN], *p; - + static const char *sizestrings[4] = { + "unknown size", "256KB", "512KB", "1MB" + }; + static const char *clockstrings[8] = { + "clock disabled", "+1 clock", "+1.5 clock", "reserved(3)", + "+2 clock", "+2.5 clock", "+3 clock", "reserved(7)" + }; + static const char *typestrings[4] = { + "flow-through burst SRAM", "reserved SRAM", + "pipelined burst SRAM", "pipelined late-write SRAM" + }; + static const char *holdstrings[4] = { + "0.5", "1.0", "(reserved2)", "(reserved3)" + }; + if ( (_get_PVR() >> 16) != 8) return -EFAULT; if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) { @@ -585,55 +599,13 @@ p += sprintf(p, " %s", (val&0x80000000)?"enabled":"disabled"); p += sprintf(p,",%sparity",(val&0x40000000)?"":"no "); - - switch( (val >> 28) & 0x3 ) - { - case 1: p += sprintf(p,",256Kb"); - break; - case 2: p += sprintf(p,",512Kb"); - break; - case 3: p += sprintf(p,",1M"); - break; - default: p += sprintf(p,",unknown size"); - break; - } - - - switch( (val >> 25) & 0x7 ) - { - case 0: p += sprintf(p,",clock disabled"); - break; - case 1: p += sprintf(p,",+1 clock"); - break; - case 2: p += sprintf(p,",+1.5 clock"); - break; - case 7: - case 3: p += sprintf(p,",reserved clock"); - break; - case 4: p += sprintf(p,",+2 clock"); - break; - case 5: p += sprintf(p,",+2.5 clock"); - break; - case 6: p += sprintf(p,",+3 clock"); - break; - } - - switch( (val >> 23) & 0x2 ) - { - case 0: p += sprintf(p,",flow-through burst SRAM"); - break; - case 1: p += sprintf(p,",reserved SRAM"); - break; - case 2: p += sprintf(p,",pipelined burst SRAM"); - break; - case 3: p += sprintf(p,",pipelined late-write SRAM"); - break; - } - - p += sprintf(p,"%s",(val>>22)?"":",data only"); - p += sprintf(p,"%s",(val>>20)?",ZZ enabled":""); - p += sprintf(p,",%s",(val>>19)?"write-through":"copy-back"); - p += sprintf(p,",%sns hold",(val>>16)?"1.0":"0.5"); + p += sprintf(p, ",%s", sizestrings[(val >> 28) & 3]); + p += sprintf(p, ",%s", clockstrings[(val >> 25) & 7]); + p += sprintf(p, ",%s", typestrings[(val >> 23) & 0x2]); + p += sprintf(p,"%s",(val>>22)&1?"":",data only"); + p += sprintf(p,"%s",(val>>20)&1?",ZZ enabled":""); + p += sprintf(p,",%s",(val>>19)&1?"write-through":"copy-back"); + p += sprintf(p,",%sns hold", holdstrings[(val>>16)&3]); p += sprintf(p,"\n"); diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c --- v2.2.11/linux/arch/ppc/kernel/ppc_ksyms.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/ppc_ksyms.c Wed Aug 25 17:29:46 1999 @@ -47,6 +47,7 @@ extern int do_signal(sigset_t *, struct pt_regs *); asmlinkage long long __ashrdi3(long long, int); +asmlinkage long long __lshrdi3(long long, int); asmlinkage int abs(int); EXPORT_SYMBOL(clear_page); @@ -187,7 +188,8 @@ EXPORT_SYMBOL(pmu_request); EXPORT_SYMBOL(pmu_poll); #ifdef CONFIG_PMAC_PBOOK -EXPORT_SYMBOL(sleep_notifier_list); +EXPORT_SYMBOL(pmu_register_sleep_notifier); +EXPORT_SYMBOL(pmu_unregister_sleep_notifier); #endif CONFIG_PMAC_PBOOK EXPORT_SYMBOL(abort); EXPORT_SYMBOL(find_devices); @@ -214,6 +216,7 @@ EXPORT_SYMBOL(device_is_compatible); EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memmove); diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c --- v2.2.11/linux/arch/ppc/kernel/prep_pci.c Fri Jun 4 13:30:47 1999 +++ linux/arch/ppc/kernel/prep_pci.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: prep_pci.c,v 1.35 1999/05/10 23:31:03 cort Exp $ + * $Id: prep_pci.c,v 1.35.2.1 1999/07/22 01:49:42 cort Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -34,6 +34,9 @@ /* How is the 82378 PIRQ mapping setup? */ unsigned char *Motherboard_routes; +void (*Motherboard_non0)(struct pci_dev *); + +void Mesquite_Map_Non0(struct pci_dev *); /* Used for Motorola to store system config register */ static unsigned long *ProcInfo; @@ -314,7 +317,7 @@ 0, /* Slot 10 - Ethernet */ 0, /* Slot 11 - Universe PCI - VME Bridge */ 3, /* Slot 12 - unused */ - 0, /* Slot 13 - unused */ + 5, /* Slot 13 - VME */ 2, /* Slot 14 - SCSI */ 0, /* Slot 15 - graphics on 3600 */ 9, /* Slot 16 - PMC */ @@ -682,8 +685,11 @@ #define MOT_RAVEN_PRESENT 0x1 #define MOT_HAWK_PRESENT 0x2 -int prep_keybd_present = 1; +/* Keyboard present flag */ +int prep_kbd_present = 1; /* Keyboard present by default */ + int MotMPIC = 0; +int mot_multi = 0; __initfunc(int raven_init(void)) { @@ -740,13 +746,18 @@ */ ProcInfo = (unsigned long *)ioremap(0xfef80400, 4); + /* Indicate to system if this is a multiprocessor board */ + if (!(*ProcInfo & MOT_PROC2_BIT)) { + mot_multi = 1; + } + /* This is a hack. If this is a 2300 or 2400 mot board then there is * no keyboard controller and we have to indicate that. */ base_mod = inb(MOTOROLA_BASETYPE_REG); if ((MotMPIC == MOT_HAWK_PRESENT) || (base_mod == 0xF9) || (base_mod == 0xFA) || (base_mod == 0xE1)) - prep_keybd_present = 0; + prep_kbd_present = 0; return 1; } @@ -759,33 +770,34 @@ const char *name; unsigned char *map; unsigned char *routes; + void (*map_non0_bus)(struct pci_dev *); /* For boards with more than bus 0 devices. */ } mot_info[] = { - {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes}, - {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes}, - {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes}, - {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes}, - {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes}, - {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes}, - {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, - {0x000, 0x00, 0x00, "", NULL, NULL} + {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes, NULL}, + {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes, NULL}, + {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes, NULL}, + {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes, NULL}, + {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes, NULL}, + {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes, NULL}, + {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes, Mesquite_Map_Non0}, + {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes, NULL}, + {0x000, 0x00, 0x00, "", NULL, NULL, NULL} }; __initfunc(unsigned long prep_route_pci_interrupts(void)) @@ -846,6 +858,7 @@ Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; Motherboard_map = mot_info[mot_entry].map; Motherboard_routes = mot_info[mot_entry].routes; + Motherboard_non0 = mot_info[mot_entry].map_non0_bus; if (!(mot_info[entry].cpu_type & 0x100)) { /* AJF adjust level/edge control according to routes */ @@ -976,6 +989,93 @@ return 0; } +static unsigned int pci_localpirqs[4] = +{ + 24, + 25, + 26, + 27 +}; + +static unsigned int pci_remotepirqs[4] = +{ + 28, + 29, + 30, + 31 +}; + +static unsigned int pci_remotedev = 0xc0; + +void +Mesquite_Map_Non0(struct pci_dev *pdev) +{ + struct pci_bus *pbus; /* Parent Bus Structure Pointer */ + unsigned int devnum; /* Accumulated Device Number */ + unsigned int irq; /* IRQ Value */ + + /* + ** Device Interrupt Line register initialization. + ** The IRQ line number will be generated after + ** taking into account all the PCI-2-PCI bridge + ** devices between the device and the Host Bridge. + */ + devnum = PCI_SLOT(pdev->devfn); + pbus = pdev->bus; + + while ((pbus->parent)->primary != (pbus->parent)->secondary) + { + devnum += PCI_SLOT((pbus->self)->devfn); + + pbus = pbus->parent; + } + + devnum &= 0x03; + + /* + ** By default, get the PCI local domain IRQ value. + */ + irq = pci_localpirqs[devnum]; + + /* + ** Determine if the device is located in the + ** remote domain or not. We must find the + ** domain's bridge device located on bus 0. + */ + pbus = pdev->bus; + + while (pbus->primary != 0) + pbus = pbus->parent; + + /* + ** Check the device/function of domain's bridge + ** device against the remote device/function. + ** If the same, then the device is located in + ** the remote domain. Thus, get the PCI remote + ** domain IRQ value. + */ + if ((pbus->self)->devfn == pci_remotedev) + irq = pci_remotepirqs[devnum]; + + /* + ** Validate the IRQ number. + */ + if (irq <= 255) + { + /* + ** Set the device's Interrupt Line register + ** to the IRQ number and save it in the + ** device's structure. + */ + + pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, (u8)irq); + + pdev->irq = irq; + + } + return; +} + __initfunc( void prep_pcibios_fixup(void)) @@ -1000,6 +1100,9 @@ if (dev->bus->number == 0) { dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, dev->irq); + } else { + if (Motherboard_non0 != NULL) + Motherboard_non0(dev); } } return; diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c --- v2.2.11/linux/arch/ppc/kernel/prep_setup.c Fri Jun 4 13:30:47 1999 +++ linux/arch/ppc/kernel/prep_setup.c Wed Aug 25 17:29:46 1999 @@ -84,7 +84,6 @@ extern unsigned char pckbd_sysrq_xlate[128]; extern void prep_setup_pci_ptrs(void); -extern void chrp_do_IRQ(struct pt_regs *regs, int cpu, int isfake); extern char saved_command_line[256]; int _prep_type; @@ -704,15 +703,27 @@ } #endif +unsigned long *MotSave_SmpIar; +unsigned char *MotSave_CpusState[2]; + __initfunc(void prep_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7)) { + RESIDUAL *old_res = (RESIDUAL *)(r3 + KERNELBASE); + /* make a copy of residual data */ if ( r3 ) { memcpy((void *)res,(void *)(r3+KERNELBASE), sizeof(RESIDUAL)); + + /* These need to be saved for the Motorola Prep + * MVME4600 and Dual MTX boards. + */ + MotSave_SmpIar = &old_res->VitalProductData.SmpIar; + MotSave_CpusState[0] = &old_res->Cpus[0].CpuState; + MotSave_CpusState[1] = &old_res->Cpus[1].CpuState; } isa_io_base = PREP_ISA_IO_BASE; diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c --- v2.2.11/linux/arch/ppc/kernel/process.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/process.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: process.c,v 1.83 1999/05/10 04:43:43 cort Exp $ + * $Id: process.c,v 1.83.2.7 1999/08/16 01:44:58 paulus Exp $ * * linux/arch/ppc/kernel/process.c * @@ -47,7 +47,6 @@ struct task_struct *last_task_used_math = NULL; static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c --- v2.2.11/linux/arch/ppc/kernel/prom.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/prom.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: prom.c,v 1.54.2.3 1999/07/02 19:58:27 cort Exp $ + * $Id: prom.c,v 1.54.2.7 1999/08/16 01:48:43 paulus Exp $ * * Procedures for interfacing to the Open Firmware PROM on * Power Macintosh computers. @@ -105,9 +105,9 @@ #ifdef CONFIG_BOOTX_TEXT -static void drawchar(char c); +void drawchar(char c); +void drawstring(const char *c); static void drawhex(unsigned long v); -static void drawstring(const char *c); static void scrollscreen(void); static void draw_byte(unsigned char c, long locX, long locY); @@ -257,6 +257,8 @@ } } +unsigned long smp_ibm_chrp_hack __initdata = 0; + /* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. @@ -292,6 +294,8 @@ #endif RELOC(boot_infos) = PTRUNRELOC(bi); + if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) + bi->logicalDisplayBase = 0; clearscreen(); @@ -345,9 +349,13 @@ } } - space = bi->deviceTreeOffset + bi->deviceTreeSize; - if (bi->ramDisk) - space = bi->ramDisk + bi->ramDiskSize; + /* Move klimit to enclose device tree, args, ramdisk, etc... */ + if (bi->version < 5) { + space = bi->deviceTreeOffset + bi->deviceTreeSize; + if (bi->ramDisk) + space = bi->ramDisk + bi->ramDiskSize; + } else + space = bi->totalParamsSize; RELOC(klimit) = PTRUNRELOC((char *) bi + space); /* New BootX will have flushed all TLBs and enters kernel with @@ -526,6 +534,7 @@ /* XXX: hack - don't start cpu 0, this cpu -- Cort */ if ( cpu++ == 0 ) continue; + RELOC(smp_ibm_chrp_hack) = 1; prom_print(RELOC("starting cpu ")); prom_print(path); *(unsigned long *)(0x4) = 0; @@ -1210,6 +1219,22 @@ } /* + * Indicates whether the root node has a given value in its + * compatible property. + */ +__openfirmware +int +machine_is_compatible(const char *compat) +{ + struct device_node *root; + + root = find_path_device("/"); + if (root == 0) + return 0; + return device_is_compatible(root, compat); +} + +/* * Find the device_node with a given phandle. */ __openfirmware @@ -1296,8 +1321,6 @@ } #endif -spinlock_t rtas_lock = SPIN_LOCK_UNLOCKED; - /* this can be called after setup -- Cort */ __openfirmware int @@ -1328,12 +1351,11 @@ for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); - - s = _disable_interrupts(); - spin_lock(&rtas_lock); + + save_flags(s); + cli(); enter_rtas((void *)__pa(&u)); - spin_unlock(&rtas_lock); - _enable_interrupts(s); + restore_flags(s); if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) outputs[i] = u.words[i+nargs+4]; @@ -1350,19 +1372,41 @@ prom_exit(); } +#ifdef CONFIG_XMON +__init +void +map_bootx_text(void) +{ + if (boot_infos == 0) + return; + boot_infos->logicalDisplayBase = + ioremap((unsigned long) boot_infos->dispDeviceBase, + boot_infos->dispDeviceRowBytes * boot_infos->dispDeviceRect[3]); +} +#endif /* CONFIG_XMON */ + /* Calc the base address of a given point (x,y) */ -#define CALC_BASE(x,y) ((BOOT_INFO_IS_V2_COMPATIBLE(bi) ? bi->logicalDisplayBase : \ - bi->dispDeviceBase) + (bi->dispDeviceRect[0] + (x)) * \ - (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * \ - ((y) + bi->dispDeviceRect[1])) +__pmac +static unsigned char * +calc_base(boot_infos_t *bi, int x, int y) +{ + unsigned char *base; -__init + base = bi->logicalDisplayBase; + if (base == 0) + base = bi->dispDeviceBase; + base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3); + base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes; + return base; +} + +__pmac static void clearscreen(void) { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *base = (unsigned long *)CALC_BASE(0,0); + unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1381,13 +1425,13 @@ __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr)); } -__init +__pmac static void flushscreen(void) { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *base = (unsigned long *)CALC_BASE(0,0); + unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1405,14 +1449,14 @@ #ifdef CONFIG_BOOTX_TEXT -__init +__pmac static void scrollscreen(void) { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *src = (unsigned long *)CALC_BASE(0,16); - unsigned long *dst = (unsigned long *)CALC_BASE(0,0); + unsigned long *src = (unsigned long *)calc_base(bi,0,16); + unsigned long *dst = (unsigned long *)calc_base(bi,0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1435,21 +1479,33 @@ } } -__init -static void +__pmac +void drawchar(char c) { unsigned long offset = reloc_offset(); - switch(c) { - case '\r': RELOC(g_loc_X) = 0; break; - case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break; - default: - draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y)); - if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) { - RELOC(g_loc_X) = 0; - RELOC(g_loc_Y)++; - } + switch (c) { + case '\b': + if (RELOC(g_loc_X) > 0) + --RELOC(g_loc_X); + break; + case '\t': + RELOC(g_loc_X) = (RELOC(g_loc_X) & -8) + 8; + break; + case '\r': + RELOC(g_loc_X) = 0; + break; + case '\n': + RELOC(g_loc_X) = 0; + RELOC(g_loc_Y)++; + break; + default: + draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y)); + } + if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) { + RELOC(g_loc_X) = 0; + RELOC(g_loc_Y)++; } while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) { scrollscreen(); @@ -1457,15 +1513,15 @@ } } -__init -static void +__pmac +void drawstring(const char *c) { - while(*c) - drawchar(*(c++)); + while (*c) + drawchar(*c++); } -__init +__pmac static void drawhex(unsigned long v) { @@ -1483,13 +1539,13 @@ } -__init +__pmac static void draw_byte(unsigned char c, long locX, long locY) { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned char *base = CALC_BASE(locX << 3, locY << 4); + unsigned char *base = calc_base(bi, locX << 3, locY << 4); unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; switch(bi->dispDeviceDepth) { @@ -1507,7 +1563,7 @@ } } -__init +__pmac static unsigned long expand_bits_8[16] = { 0x00000000, 0x000000ff, @@ -1527,7 +1583,7 @@ 0xffffffff }; -__init +__pmac static unsigned long expand_bits_16[4] = { 0x00000000, 0x0000ffff, @@ -1536,7 +1592,7 @@ }; -__init +__pmac static void draw_byte_32(unsigned char *font, unsigned long *base) { @@ -1562,7 +1618,7 @@ } } -__init +__pmac static void draw_byte_16(unsigned char *font, unsigned long *base) { @@ -1584,7 +1640,7 @@ } } -__init +__pmac static void draw_byte_8(unsigned char *font, unsigned long *base) { @@ -1604,7 +1660,7 @@ } } -__init +__pmac static unsigned char vga_font[cmapsz] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c --- v2.2.11/linux/arch/ppc/kernel/setup.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/setup.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.132.2.1 1999/06/03 03:03:45 paulus Exp $ + * $Id: setup.c,v 1.132.2.2 1999/07/20 05:04:47 paulus Exp $ * Common prep/pmac/chrp boot and setup code. */ diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/sleep.S linux/arch/ppc/kernel/sleep.S --- v2.2.11/linux/arch/ppc/kernel/sleep.S Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/kernel/sleep.S Wed Aug 25 17:29:46 1999 @@ -0,0 +1,264 @@ +/* + * This file contains sleep low-level functions for PowerBook G3. + * Copyright (C) 1999 Benjamin Herrenschmidt (bh40@calva.net) + * and Paul Mackerras (paulus@cs.anu.edu.au). + * + * 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 "ppc_asm.tmpl" +#include +#include + +#define MAGIC 0x4c617273 /* 'Lars' */ + +/* + * Structure for storing CPU registers on the stack. + */ +#define SL_SP 0 +#define SL_PC 4 +#define SL_MSR 8 +#define SL_SDR1 0xc +#define SL_SPRG0 0x10 /* 4 sprg's */ +#define SL_DBAT0 0x20 +#define SL_IBAT0 0x28 +#define SL_DBAT1 0x30 +#define SL_IBAT1 0x38 +#define SL_DBAT2 0x40 +#define SL_IBAT2 0x48 +#define SL_DBAT3 0x50 +#define SL_IBAT3 0x58 +#define SL_TB 0x60 +#define SL_HID0 0x68 +#define SL_R2 0x6c +#define SL_R12 0x70 /* r12 to r31 */ +#define SL_SIZE (SL_R12 + 80) + +#define tophys(rd,rs) addis rd,rs,-KERNELBASE@h +#define tovirt(rd,rs) addis rd,rs,KERNELBASE@h + + .text + +_GLOBAL(low_sleep_handler) + mflr r0 + stw r0,4(r1) + stwu r1,-SL_SIZE(r1) + stw r2,SL_R2(r1) + stmw r12,SL_R12(r1) + + /* Save MSR, SDR1, TB */ + mfmsr r4 + stw r4,SL_MSR(r1) + mfsdr1 r4 + stw r4,SL_SDR1(r1) +1: + mftbu r4 + stw r4,SL_TB(r1) + mftb r5 + stw r5,SL_TB+4(r1) + mftbu r3 + cmpw r3,r4 + bne 1b + + /* Save SPRGs */ + mfsprg r4,0 + stw r4,SL_SPRG0(r1) + mfsprg r4,1 + stw r4,SL_SPRG0+4(r1) + mfsprg r4,2 + stw r4,SL_SPRG0+8(r1) + mfsprg r4,3 + stw r4,SL_SPRG0+12(r1) + + /* Save BATs */ + mfdbatu r4,0 + stw r4,SL_DBAT0(r1) + mfdbatl r4,0 + stw r4,SL_DBAT0+4(r1) + mfdbatu r4,1 + stw r4,SL_DBAT1(r1) + mfdbatl r4,1 + stw r4,SL_DBAT1+4(r1) + mfdbatu r4,2 + stw r4,SL_DBAT2(r1) + mfdbatl r4,2 + stw r4,SL_DBAT2+4(r1) + mfdbatu r4,3 + stw r4,SL_DBAT3(r1) + mfdbatl r4,3 + stw r4,SL_DBAT3+4(r1) + mfibatu r4,0 + stw r4,SL_IBAT0(r1) + mfibatl r4,0 + stw r4,SL_IBAT0+4(r1) + mfibatu r4,1 + stw r4,SL_IBAT1(r1) + mfibatl r4,1 + stw r4,SL_IBAT1+4(r1) + mfibatu r4,2 + stw r4,SL_IBAT2(r1) + mfibatl r4,2 + stw r4,SL_IBAT2+4(r1) + mfibatu r4,3 + stw r4,SL_IBAT3(r1) + mfibatl r4,3 + stw r4,SL_IBAT3+4(r1) + + /* Save HID0 */ + mfspr r4,HID0 + stw r4,SL_HID0(r1) + + /* Set up stuff at address 0 */ + lis r5,wake_up@ha + addi r5,r5,wake_up@l + tophys(r5,r5) + stw r5,SL_PC(r1) + lis r4,KERNELBASE@h + tophys(r5,r1) + addi r5,r5,SL_PC + lis r6,MAGIC@ha + addi r6,r6,MAGIC@l + stw r5,0(r4) + stw r6,4(r4) + +/* + * Flush the L1 data cache by reading the first 64kB of RAM + * and then flushing the same area with the dcbf instruction. + * The L2 cache has already been disabled. + */ + li r4,0x0800 /* 64kB / 32B */ + mtctr r4 + lis r4,KERNELBASE@h +1: + lwz r0,0(r4) + addi r4,r4,0x0020 /* Go to start of next cache line */ + bdnz 1b + sync + + li r4,0x0800 /* 64k */ + mtctr r4 + lis r4,KERNELBASE@h +1: + dcbf r0,r4 + addi r4,r4,0x0020 /* Go to start of next cache line */ + bdnz 1b + sync + +/* + * Set the HID0 and MSR for sleep. + */ + mfspr r2,HID0 + rlwinm r2,r2,0,10,7 /* clear doze, nap */ + oris r2,r2,HID0_SLEEP@h + sync + mtspr HID0,r2 + sync + + mfmsr r2 + oris r2,r2,MSR_POW@h +1: sync + mtmsr r2 + isync + b 1b + +/* + * Here is the resume code. + * r1 has the physical address of SL_PC(sp). + */ + +wake_up: + /* Restore the HID0 register. This turns on the L1 caches. */ + subi r1,r1,SL_PC + lwz r3,SL_HID0(r1) + sync + isync + mtspr HID0,r3 + sync + + /* Restore the kernel's segment registers, the + BATs, and SDR1. Then we can turn on the MMU. */ + 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 + + lwz r4,SL_SDR1(r1) + mtsdr1 r4 + lwz r4,SL_SPRG0(r1) + mtsprg 0,r4 + lwz r4,SL_SPRG0+4(r1) + mtsprg 1,r4 + lwz r4,SL_SPRG0+8(r1) + mtsprg 2,r4 + lwz r4,SL_SPRG0+12(r1) + mtsprg 3,r4 + + lwz r4,SL_DBAT0(r1) + mtdbatu 0,r4 + lwz r4,SL_DBAT0+4(r1) + mtdbatl 0,r4 + lwz r4,SL_DBAT1(r1) + mtdbatu 1,r4 + lwz r4,SL_DBAT1+4(r1) + mtdbatl 1,r4 + lwz r4,SL_DBAT2(r1) + mtdbatu 2,r4 + lwz r4,SL_DBAT2+4(r1) + mtdbatl 2,r4 + lwz r4,SL_DBAT3(r1) + mtdbatu 3,r4 + lwz r4,SL_DBAT3+4(r1) + mtdbatl 3,r4 + lwz r4,SL_IBAT0(r1) + mtibatu 0,r4 + lwz r4,SL_IBAT0+4(r1) + mtibatl 0,r4 + lwz r4,SL_IBAT1(r1) + mtibatu 1,r4 + lwz r4,SL_IBAT1+4(r1) + mtibatl 1,r4 + lwz r4,SL_IBAT2(r1) + mtibatu 2,r4 + lwz r4,SL_IBAT2+4(r1) + mtibatl 2,r4 + lwz r4,SL_IBAT3(r1) + mtibatu 3,r4 + lwz r4,SL_IBAT3+4(r1) + mtibatl 3,r4 + + /* restore the MSR and turn on the MMU */ + lwz r3,SL_MSR(r1) + bl turn_on_mmu + + /* get back the stack pointer */ + tovirt(r1,r1) + + /* Restore TB */ + lwz r3,SL_TB(r1) + lwz r4,SL_TB+4(r1) + mttbu r3 + mttbl r4 + + /* Restore the callee-saved registers and return */ + lwz r2,SL_R2(r1) + lmw r12,SL_R12(r1) + addi r1,r1,SL_SIZE + lwz r0,4(r1) + mtlr r0 + blr + +turn_on_mmu: + mflr r4 + tovirt(r4,r4) + mtsrr0 r4 + mtsrr1 r3 + sync + rfi diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c --- v2.2.11/linux/arch/ppc/kernel/smp.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/smp.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: smp.c,v 1.49.2.3 1999/06/24 17:12:55 cort Exp $ + * $Id: smp.c,v 1.49.2.5 1999/07/22 01:49:45 cort Exp $ * * Smp support for ppc. * @@ -57,6 +57,10 @@ extern int cpu_idle(void *unused); u_int openpic_read(volatile u_int *addr); +extern int mot_multi; +extern unsigned long *MotSave_SmpIar; +extern unsigned char *MotSave_CpusState[2]; + /* register for interrupting the secondary processor on the powersurge */ #define PSURGE_INTR ((volatile unsigned *)0xf80000c0) @@ -171,7 +175,7 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) { int i; - if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) + if ( !(_machine & (_MACH_Pmac|_MACH_chrp|_MACH_prep)) ) return; spin_lock(&mesg_pass_lock); @@ -197,8 +201,8 @@ break; } - if ( _machine == _MACH_Pmac ) - { + switch (_machine) { + case _MACH_Pmac: /* interrupt secondary processor */ out_be32(PSURGE_INTR, ~0); out_be32(PSURGE_INTR, 0); @@ -208,10 +212,10 @@ */ /* interrupt primary */ /**(volatile unsigned long *)(0xf3019000);*/ - } + break; - if ( _machine == _MACH_chrp ) - { + case _MACH_chrp: + case _MACH_prep: /* * There has to be some way of doing this better - * perhaps a sent-to-all or send-to-all-but-self @@ -234,6 +238,7 @@ openpic_cause_IPI(target, 0, 1U << target); break; } + break; } spin_unlock(&mesg_pass_lock); @@ -273,23 +278,27 @@ */ cacheflush_time = 5 * 1024; - if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) - { - printk("SMP not supported on this machine.\n"); - return; - } - switch ( _machine ) { case _MACH_Pmac: /* assume powersurge board - 2 processors -- Cort */ - cpu_nr = 2; + cpu_nr = 2; break; case _MACH_chrp: cpu_nr = ((openpic_read(&OpenPIC->Global.Feature_Reporting0) & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT)+1; break; + case _MACH_prep: + /* assume 2 for now == fix later -- Johnnie */ + if ( mot_multi ) + { + cpu_nr = 2; + break; + } + default: + printk("SMP not supported on this machine.\n"); + return; } /* @@ -343,6 +352,11 @@ __pa(__secondary_start_chrp), i); #endif break; + case _MACH_prep: + *MotSave_SmpIar = (unsigned long)__secondary_start_psurge - KERNELBASE; + *MotSave_CpusState[1] = CPU_GOOD; + printk("CPU1 reset, waiting\n"); + break; } /* @@ -399,12 +413,12 @@ { smp_store_cpu_info(current->processor); set_dec(decrementer_count); - #if 0 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; #endif + init_idle(); cpu_callin_map[current->processor] = 1; while(!smp_commenced) barrier(); diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/syscalls.c linux/arch/ppc/kernel/syscalls.c --- v2.2.11/linux/arch/ppc/kernel/syscalls.c Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/kernel/syscalls.c Wed Aug 25 17:29:46 1999 @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v2.2.11/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c --- v2.2.11/linux/arch/ppc/kernel/time.c Fri Jun 4 13:30:47 1999 +++ linux/arch/ppc/kernel/time.c Wed Aug 25 17:29:46 1999 @@ -1,5 +1,5 @@ /* - * $Id: time.c,v 1.47.2.1 1999/05/29 19:10:23 cort Exp $ + * $Id: time.c,v 1.47.2.2 1999/08/14 21:45:49 cort Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -110,15 +110,14 @@ /* * update the rtc when needed */ - if ( xtime.tv_sec > last_rtc_update + 660 ) + if ( (time_status & STA_UNSYNC) && + (xtime.tv_sec > last_rtc_update + 660) ) { - if (ppc_md.set_rtc_time(xtime.tv_sec) == 0) { + if (ppc_md.set_rtc_time(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; - } - else { + else /* do it again in 60 s */ last_rtc_update = xtime.tv_sec - 60; - } } } } @@ -168,7 +167,6 @@ int frac_tick; last_rtc_update = 0; /* so the rtc gets updated soon */ - frac_tick = tv->tv_usec % (1000000 / HZ); save_flags(flags); cli(); @@ -332,6 +330,3 @@ */ GregorianDay(tm); } - - - diff -u --recursive --new-file v2.2.11/linux/arch/ppc/pmac_defconfig linux/arch/ppc/pmac_defconfig --- v2.2.11/linux/arch/ppc/pmac_defconfig Mon Aug 9 16:05:55 1999 +++ linux/arch/ppc/pmac_defconfig Wed Aug 25 17:29:46 1999 @@ -245,6 +245,7 @@ # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set # CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set # # Ethernet (10 or 100Mbit) @@ -257,6 +258,7 @@ # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set +# CONFIG_SIS900 is not set # CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/Makefile linux/arch/ppc/xmon/Makefile --- v2.2.11/linux/arch/ppc/xmon/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/Makefile Wed Aug 25 17:29:46 1999 @@ -0,0 +1,6 @@ +# Makefile for xmon + +O_TARGET = x.o +O_OBJS = start.o xmon.o ppc-dis.o ppc-opc.o subr_prf.o setjmp.o + +include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/adb.c linux/arch/ppc/xmon/adb.c --- v2.2.11/linux/arch/ppc/xmon/adb.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/adb.c Wed Aug 25 17:29:46 1999 @@ -0,0 +1,212 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ +#include "nonstdio.h" +#include "privinst.h" + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +#define ADB_B (*(volatile unsigned char *)0xf3016000) +#define ADB_SR (*(volatile unsigned char *)0xf3017400) +#define ADB_ACR (*(volatile unsigned char *)0xf3017600) +#define ADB_IFR (*(volatile unsigned char *)0xf3017a00) + +static inline void eieio(void) { asm volatile ("eieio" : :); } + +#define N_ADB_LOG 1000 +struct adb_log { + unsigned char b; + unsigned char ifr; + unsigned char acr; + unsigned int time; +} adb_log[N_ADB_LOG]; +int n_adb_log; + +void +init_adb_log(void) +{ + adb_log[0].b = ADB_B; + adb_log[0].ifr = ADB_IFR; + adb_log[0].acr = ADB_ACR; + adb_log[0].time = get_dec(); + n_adb_log = 0; +} + +void +dump_adb_log(void) +{ + unsigned t, t0; + struct adb_log *ap; + int i; + + ap = adb_log; + t0 = ap->time; + for (i = 0; i <= n_adb_log; ++i, ++ap) { + t = t0 - ap->time; + printf("b=%x ifr=%x acr=%x at %d.%.7d\n", ap->b, ap->ifr, ap->acr, + t / 1000000000, (t % 1000000000) / 100); + } +} + +void +adb_chklog(void) +{ + struct adb_log *ap = &adb_log[n_adb_log + 1]; + + ap->b = ADB_B; + ap->ifr = ADB_IFR; + ap->acr = ADB_ACR; + if (ap->b != ap[-1].b || (ap->ifr & 4) != (ap[-1].ifr & 4) + || ap->acr != ap[-1].acr) { + ap->time = get_dec(); + ++n_adb_log; + } +} + +int +adb_bitwait(int bmask, int bval, int fmask, int fval) +{ + int i; + struct adb_log *ap; + + for (i = 10000; i > 0; --i) { + adb_chklog(); + ap = &adb_log[n_adb_log]; + if ((ap->b & bmask) == bval && (ap->ifr & fmask) == fval) + return 0; + } + return -1; +} + +int +adb_wait(void) +{ + if (adb_bitwait(0, 0, 4, 4) < 0) { + printf("adb: ready wait timeout\n"); + return -1; + } + return 0; +} + +void +adb_readin(void) +{ + int i, j; + unsigned char d[64]; + + if (ADB_B & 8) { + printf("ADB_B: %x\n", ADB_B); + return; + } + i = 0; + adb_wait(); + j = ADB_SR; + eieio(); + ADB_B &= ~0x20; + eieio(); + for (;;) { + if (adb_wait() < 0) + break; + d[i++] = ADB_SR; + eieio(); + if (ADB_B & 8) + break; + ADB_B ^= 0x10; + eieio(); + } + ADB_B |= 0x30; + if (adb_wait() == 0) + j = ADB_SR; + for (j = 0; j < i; ++j) + printf("%.2x ", d[j]); + printf("\n"); +} + +int +adb_write(unsigned char *d, int i) +{ + int j; + unsigned x; + + if ((ADB_B & 8) == 0) { + printf("r: "); + adb_readin(); + } + for (;;) { + ADB_ACR = 0x1c; + eieio(); + ADB_SR = d[0]; + eieio(); + ADB_B &= ~0x20; + eieio(); + if (ADB_B & 8) + break; + ADB_ACR = 0xc; + eieio(); + ADB_B |= 0x20; + eieio(); + adb_readin(); + } + adb_wait(); + for (j = 1; j < i; ++j) { + ADB_SR = d[j]; + eieio(); + ADB_B ^= 0x10; + eieio(); + if (adb_wait() < 0) + break; + } + ADB_ACR = 0xc; + eieio(); + x = ADB_SR; + eieio(); + ADB_B |= 0x30; + return j; +} + +void +adbcmds(void) +{ + char cmd; + unsigned rtcu, rtcl, dec, pdec, x; + int i, j; + unsigned char d[64]; + + cmd = skipbl(); + switch (cmd) { + case 't': + for (;;) { + rtcl = get_rtcl(); + rtcu = get_rtcu(); + dec = get_dec(); + printf("rtc u=%u l=%u dec=%x (%d = %d.%.7d)\n", + rtcu, rtcl, dec, pdec - dec, (pdec - dec) / 1000000000, + ((pdec - dec) % 1000000000) / 100); + pdec = dec; + if (cmd == 'x') + break; + while (xmon_read(stdin, &cmd, 1) != 1) + ; + } + break; + case 'r': + init_adb_log(); + while (adb_bitwait(8, 0, 0, 0) == 0) + adb_readin(); + break; + case 'w': + i = 0; + while (scanhex(&x)) + d[i++] = x; + init_adb_log(); + j = adb_write(d, i); + printf("sent %d bytes\n", j); + while (adb_bitwait(8, 0, 0, 0) == 0) + adb_readin(); + break; + case 'l': + dump_adb_log(); + break; + } +} diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/ansidecl.h linux/arch/ppc/xmon/ansidecl.h --- v2.2.11/linux/arch/ppc/xmon/ansidecl.h Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/ansidecl.h Wed Aug 25 17:29:46 1999 @@ -0,0 +1,141 @@ +/* ANSI and traditional C compatability macros + Copyright 1991, 1992 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* ANSI and traditional C compatibility macros + + ANSI C is assumed if __STDC__ is #defined. + + Macro ANSI C definition Traditional C definition + ----- ---- - ---------- ----------- - ---------- + PTR `void *' `char *' + LONG_DOUBLE `long double' `double' + VOLATILE `volatile' `' + SIGNED `signed' `' + PTRCONST `void *const' `char *' + ANSI_PROTOTYPES 1 not defined + + CONST is also defined, but is obsolete. Just use const. + + DEFUN (name, arglist, args) + + Defines function NAME. + + ARGLIST lists the arguments, separated by commas and enclosed in + parentheses. ARGLIST becomes the argument list in traditional C. + + ARGS list the arguments with their types. It becomes a prototype in + ANSI C, and the type declarations in traditional C. Arguments should + be separated with `AND'. For functions with a variable number of + arguments, the last thing listed should be `DOTS'. + + DEFUN_VOID (name) + + Defines a function NAME, which takes no arguments. + + obsolete -- EXFUN (name, (prototype)) -- obsolete. + + Replaced by PARAMS. Do not use; will disappear someday soon. + Was used in external function declarations. + In ANSI C it is `NAME PROTOTYPE' (so PROTOTYPE should be enclosed in + parentheses). In traditional C it is `NAME()'. + For a function that takes no arguments, PROTOTYPE should be `(void)'. + + PARAMS ((args)) + + We could use the EXFUN macro to handle prototype declarations, but + the name is misleading and the result is ugly. So we just define a + simple macro to handle the parameter lists, as in: + + static int foo PARAMS ((int, char)); + + This produces: `static int foo();' or `static int foo (int, char);' + + EXFUN would have done it like this: + + static int EXFUN (foo, (int, char)); + + but the function is not external...and it's hard to visually parse + the function name out of the mess. EXFUN should be considered + obsolete; new code should be written to use PARAMS. + + For example: + extern int printf PARAMS ((CONST char *format DOTS)); + int DEFUN(fprintf, (stream, format), + FILE *stream AND CONST char *format DOTS) { ... } + void DEFUN_VOID(abort) { ... } +*/ + +#ifndef _ANSIDECL_H + +#define _ANSIDECL_H 1 + + +/* Every source file includes this file, + so they will all get the switch for lint. */ +/* LINTLIBRARY */ + + +#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) +/* All known AIX compilers implement these things (but don't always + define __STDC__). The RISC/OS MIPS compiler defines these things + in SVR4 mode, but does not define __STDC__. */ + +#define PTR void * +#define PTRCONST void *CONST +#define LONG_DOUBLE long double + +#define AND , +#define NOARGS void +#define CONST const +#define VOLATILE volatile +#define SIGNED signed +#define DOTS , ... + +#define EXFUN(name, proto) name proto +#define DEFUN(name, arglist, args) name(args) +#define DEFUN_VOID(name) name(void) + +#define PROTO(type, name, arglist) type name arglist +#define PARAMS(paramlist) paramlist +#define ANSI_PROTOTYPES 1 + +#else /* Not ANSI C. */ + +#define PTR char * +#define PTRCONST PTR +#define LONG_DOUBLE double + +#define AND ; +#define NOARGS +#define CONST +#ifndef const /* some systems define it in header files for non-ansi mode */ +#define const +#endif +#define VOLATILE +#define SIGNED +#define DOTS + +#define EXFUN(name, proto) name() +#define DEFUN(name, arglist, args) name arglist args; +#define DEFUN_VOID(name) name() +#define PROTO(type, name, arglist) type name () +#define PARAMS(paramlist) () + +#endif /* ANSI C. */ + +#endif /* ansidecl.h */ diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/nonstdio.h linux/arch/ppc/xmon/nonstdio.h --- v2.2.11/linux/arch/ppc/xmon/nonstdio.h Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/nonstdio.h Wed Aug 25 17:29:46 1999 @@ -0,0 +1,22 @@ +typedef int FILE; +extern FILE *xmon_stdin, *xmon_stdout; +#define EOF (-1) +#define stdin xmon_stdin +#define stdout xmon_stdout +#define printf xmon_printf +#define fprintf xmon_fprintf +#define fputs xmon_fputs +#define fgets xmon_fgets +#define putchar xmon_putchar +#define getchar xmon_getchar +#define putc xmon_putc +#define getc xmon_getc +#define fopen(n, m) NULL +#define fflush(f) do {} while (0) +#define fclose(f) do {} while (0) +extern char *fgets(char *, int, void *); +extern void xmon_printf(const char *, ...); +extern void xmon_fprintf(void *, const char *, ...); +extern void xmon_sprintf(char *, const char *, ...); + +#define perror(s) printf("%s: no files!\n", (s)) diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/ppc-dis.c linux/arch/ppc/xmon/ppc-dis.c --- v2.2.11/linux/arch/ppc/xmon/ppc-dis.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/ppc-dis.c Wed Aug 25 17:29:46 1999 @@ -0,0 +1,190 @@ +/* ppc-dis.c -- Disassemble PowerPC instructions + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them 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. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "nonstdio.h" +#include "ansidecl.h" +#include "ppc.h" + +static int print_insn_powerpc PARAMS ((FILE *, unsigned long insn, + unsigned memaddr, int dialect)); + +extern void print_address PARAMS((unsigned memaddr)); + +/* Print a big endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601. */ + +int +print_insn_big_powerpc (FILE *out, unsigned long insn, unsigned memaddr) +{ + return print_insn_powerpc (out, insn, memaddr, + PPC_OPCODE_PPC | PPC_OPCODE_601); +} + +/* Print a PowerPC or POWER instruction. */ + +static int +print_insn_powerpc (FILE *out, unsigned long insn, unsigned memaddr, + int dialect) +{ + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; + + /* Get the major opcode of the instruction. */ + op = PPC_OP (insn); + + /* Find the first match in the opcode table. We could speed this up + a bit by doing a binary search on the major opcode. */ + opcode_end = powerpc_opcodes + powerpc_num_opcodes; + for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + { + unsigned long table_op; + const unsigned char *opindex; + const struct powerpc_operand *operand; + int invalid; + int need_comma; + int need_paren; + + table_op = PPC_OP (opcode->opcode); + if (op < table_op) + break; + if (op > table_op) + continue; + + if ((insn & opcode->mask) != opcode->opcode + || (opcode->flags & dialect) == 0) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + operand = powerpc_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + fprintf(out, "%s", opcode->name); + if (opcode->operands[0] != 0) + fprintf(out, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + need_paren = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + long value; + + operand = powerpc_operands + *opindex; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & PPC_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) 0); + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0 + && (value & (1 << (operand->bits - 1))) != 0) + value -= 1 << operand->bits; + } + + /* If the operand is optional, and the value is zero, don't + print anything. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && (operand->flags & PPC_OPERAND_NEXT) == 0 + && value == 0) + continue; + + if (need_comma) + { + fprintf(out, ","); + need_comma = 0; + } + + /* Print the operand as directed by the flags. */ + if ((operand->flags & PPC_OPERAND_GPR) != 0) + fprintf(out, "r%ld", value); + else if ((operand->flags & PPC_OPERAND_FPR) != 0) + fprintf(out, "f%ld", value); + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) + print_address (memaddr + value); + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + print_address (value & 0xffffffff); + else if ((operand->flags & PPC_OPERAND_CR) == 0 + || (dialect & PPC_OPCODE_PPC) == 0) + fprintf(out, "%ld", value); + else + { + if (operand->bits == 3) + fprintf(out, "cr%d", value); + else + { + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + fprintf(out, "4*cr%d", cr); + cc = value & 3; + if (cc != 0) + { + if (cr != 0) + fprintf(out, "+"); + fprintf(out, "%s", cbnames[cc]); + } + } + } + + if (need_paren) + { + fprintf(out, ")"); + need_paren = 0; + } + + if ((operand->flags & PPC_OPERAND_PARENS) == 0) + need_comma = 1; + else + { + fprintf(out, "("); + need_paren = 1; + } + } + + /* We have found and printed an instruction; return. */ + return 4; + } + + /* We could not find a match. */ + fprintf(out, ".long 0x%lx", insn); + + return 4; +} diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/ppc-opc.c linux/arch/ppc/xmon/ppc-opc.c --- v2.2.11/linux/arch/ppc/xmon/ppc-opc.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/ppc-opc.c Wed Aug 25 17:29:46 1999 @@ -0,0 +1,2816 @@ +/* ppc-opc.c -- PowerPC opcode list + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them 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. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include +#include "ansidecl.h" +#include "ppc.h" + +/* This file holds the PowerPC opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bat PARAMS ((unsigned long, long, const char **)); +static long extract_bat PARAMS ((unsigned long, int *)); +static unsigned long insert_bba PARAMS ((unsigned long, long, const char **)); +static long extract_bba PARAMS ((unsigned long, int *)); +static unsigned long insert_bd PARAMS ((unsigned long, long, const char **)); +static long extract_bd PARAMS ((unsigned long, int *)); +static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **)); +static long extract_bdm PARAMS ((unsigned long, int *)); +static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **)); +static long extract_bdp PARAMS ((unsigned long, int *)); +static unsigned long insert_bo PARAMS ((unsigned long, long, const char **)); +static long extract_bo PARAMS ((unsigned long, int *)); +static unsigned long insert_boe PARAMS ((unsigned long, long, const char **)); +static long extract_boe PARAMS ((unsigned long, int *)); +static unsigned long insert_ds PARAMS ((unsigned long, long, const char **)); +static long extract_ds PARAMS ((unsigned long, int *)); +static unsigned long insert_li PARAMS ((unsigned long, long, const char **)); +static long extract_li PARAMS ((unsigned long, int *)); +static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **)); +static long extract_mbe PARAMS ((unsigned long, int *)); +static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **)); +static long extract_mb6 PARAMS ((unsigned long, int *)); +static unsigned long insert_nb PARAMS ((unsigned long, long, const char **)); +static long extract_nb PARAMS ((unsigned long, int *)); +static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **)); +static long extract_nsi PARAMS ((unsigned long, int *)); +static unsigned long insert_ral PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ram PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ras PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **)); +static long extract_rbs PARAMS ((unsigned long, int *)); +static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **)); +static long extract_sh6 PARAMS ((unsigned long, int *)); +static unsigned long insert_spr PARAMS ((unsigned long, long, const char **)); +static long extract_spr PARAMS ((unsigned long, int *)); +static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **)); +static long extract_tbr PARAMS ((unsigned long, int *)); + +/* The operands table. + + The fields are bits, shift, signed, insert, extract, flags. */ + +const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED (0) + { 0, 0, 0, 0, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA (1) +#define BA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BA field in an XL form instruction when it must be the same + as the BT field in the same instruction. */ +#define BAT (2) + { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE }, + + /* The BB field in an XL form instruction. */ +#define BB (3) +#define BB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_CR }, + + /* The BB field in an XL form instruction when it must be the same + as the BA field in the same instruction. */ +#define BBA (4) + { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD (5) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when absolute addressing is + used. */ +#define BDA (6) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM (7) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used + and absolute address is used. */ +#define BDMA (8) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDP (9) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used + and absolute addressing is used. */ +#define BDPA (10) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF (11) + { 3, 23, 0, 0, PPC_OPERAND_CR }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF (12) + { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BFA field in an X or XL form instruction. */ +#define BFA (13) + { 3, 18, 0, 0, PPC_OPERAND_CR }, + + /* The BI field in a B form or XL form instruction. */ +#define BI (14) +#define BI_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO (15) +#define BO_MASK (0x1f << 21) + { 5, 21, insert_bo, extract_bo, 0 }, + + /* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. */ +#define BOE (16) + { 5, 21, insert_boe, extract_boe, 0 }, + + /* The BT field in an X or XL form instruction. */ +#define BT (17) + { 5, 21, 0, 0, PPC_OPERAND_CR }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR (18) + { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D (19) + { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#define DS (20) + { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The FL1 field in a POWER SC form instruction. */ +#define FL1 (21) + { 4, 12, 0, 0, 0 }, + + /* The FL2 field in a POWER SC form instruction. */ +#define FL2 (22) + { 3, 2, 0, 0, 0 }, + + /* The FLM field in an XFL form instruction. */ +#define FLM (23) + { 8, 17, 0, 0, 0 }, + + /* The FRA field in an X or A form instruction. */ +#define FRA (24) +#define FRA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB (25) +#define FRB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC (26) +#define FRC_MASK (0x1f << 6) + { 5, 6, 0, 0, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS (27) +#define FRT (FRS) + { 5, 21, 0, 0, PPC_OPERAND_FPR }, + + /* The FXM field in an XFX instruction. */ +#define FXM (28) +#define FXM_MASK (0xff << 12) + { 8, 12, 0, 0, 0 }, + + /* The L field in a D or X form instruction. */ +#define L (29) + { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL }, + + /* The LEV field in a POWER SC form instruction. */ +#define LEV (30) + { 7, 5, 0, 0, 0 }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI (31) + { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The LI field in an I form instruction when used as an absolute + address. */ +#define LIA (32) + { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The MB field in an M form instruction. */ +#define MB (33) +#define MB_MASK (0x1f << 6) + { 5, 6, 0, 0, 0 }, + + /* The ME field in an M form instruction. */ +#define ME (34) +#define ME_MASK (0x1f << 1) + { 5, 1, 0, 0, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE (35) + { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + { 32, 0, insert_mbe, extract_mbe, 0 }, + + /* The MB or ME field in an MD or MDS form instruction. The high + bit is wrapped to the low end. */ +#define MB6 (37) +#define ME6 (MB6) +#define MB6_MASK (0x3f << 5) + { 6, 5, insert_mb6, extract_mb6, 0 }, + + /* The NB field in an X form instruction. The value 32 is stored as + 0. */ +#define NB (38) + { 6, 11, insert_nb, extract_nb, 0 }, + + /* The NSI field in a D form instruction. This is the same as the + SI field, only negated. */ +#define NSI (39) + { 16, 0, insert_nsi, extract_nsi, + PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED }, + + /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */ +#define RA (40) +#define RA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ +#define RAL (41) + { 5, 16, insert_ral, 0, PPC_OPERAND_GPR }, + + /* The RA field in an lmw instruction, which has special value + restrictions. */ +#define RAM (42) + { 5, 16, insert_ram, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS (43) + { 5, 16, insert_ras, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB (44) +#define RB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS (45) + { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS (46) +#define RT (RS) +#define RT_MASK (0x1f << 21) + { 5, 21, 0, 0, PPC_OPERAND_GPR }, + + /* The SH field in an X or M form instruction. */ +#define SH (47) +#define SH_MASK (0x1f << 11) + { 5, 11, 0, 0, 0 }, + + /* The SH field in an MD form instruction. This is split. */ +#define SH6 (48) +#define SH6_MASK ((0x1f << 11) | (1 << 1)) + { 6, 1, insert_sh6, extract_sh6, 0 }, + + /* The SI field in a D form instruction. */ +#define SI (49) + { 16, 0, 0, 0, PPC_OPERAND_SIGNED }, + + /* The SI field in a D form instruction when we accept a wide range + of positive values. */ +#define SISIGNOPT (50) + { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT }, + + /* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ +#define SPR (51) +#define SPR_MASK (0x3ff << 11) + { 10, 11, insert_spr, extract_spr, 0 }, + + /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */ +#define SPRBAT (52) +#define SPRBAT_MASK (0x3 << 17) + { 2, 17, 0, 0, 0 }, + + /* The SPRG register number in an XFX form m[ft]sprg instruction. */ +#define SPRG (53) +#define SPRG_MASK (0x3 << 16) + { 2, 16, 0, 0, 0 }, + + /* The SR field in an X form instruction. */ +#define SR (54) + { 4, 16, 0, 0, 0 }, + + /* The SV field in a POWER SC form instruction. */ +#define SV (55) + { 14, 2, 0, 0, 0 }, + + /* The TBR field in an XFX form instruction. This is like the SPR + field, but it is optional. */ +#define TBR (56) + { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL }, + + /* The TO field in a D or X form instruction. */ +#define TO (57) +#define TO_MASK (0x1f << 21) + { 5, 21, 0, 0, 0 }, + + /* The U field in an X form instruction. */ +#define U (58) + { 4, 12, 0, 0, 0 }, + + /* The UI field in a D form instruction. */ +#define UI (59) + { 16, 0, 0, 0, 0 }, +}; + +/* The functions used to insert and extract complicated operands. */ + +/* The BA field in an XL form instruction when it must be the same as + the BT field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BT field into the BA field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bat (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static long +extract_bat (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BB field in an XL form instruction when it must be the same as + the BA field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BA field into the BB field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bba (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 16) & 0x1f) << 11); +} + +static long +extract_bba (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BD field in a B form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_bd (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_bd (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + We must set the y bit of the BO field to 1 if the offset is + negative. When extracting, we require that the y bit be 1 and that + the offset be positive, since if the y bit is 0 we just want to + print the normal form of the instruction. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdm (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 0x8000) != 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdm (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) == 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the + modifier is used. + This is like BDM, above, except that the branch is expected to be + taken. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdp (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 0x8000) == 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdp (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) != 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* Check for legal values of a BO field. */ + +static int +valid_bo (value) + long value; +{ + /* Certain encodings have bits that are required to be zero. These + are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + return 1; + case 0x4: + return (value & 0x2) == 0; + case 0x10: + return (value & 0x8) == 0; + case 0x14: + return value == 0x14; + } +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL + && ! valid_bo (value)) + *errmsg = "invalid conditional option"; + return insn | ((value & 0x1f) << 21); +} + +static long +extract_bo (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value; +} + +/* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. When + extracting it, we force it to be even. */ + +static unsigned long +insert_boe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL) + { + if (! valid_bo (value)) + *errmsg = "invalid conditional option"; + else if ((value & 1) != 0) + *errmsg = "attempt to set y bit when using + or - modifier"; + } + return insn | ((value & 0x1f) << 21); +} + +static long +extract_boe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value & 0x1e; +} + +/* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_ds (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_ds (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The LI field in an I form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_li (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0x3fffffc); +} + +/*ARGSUSED*/ +static long +extract_li (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x2000000) != 0) + return (insn & 0x3fffffc) - 0x4000000; + else + return insn & 0x3fffffc; +} + +/* The MB and ME fields in an M form instruction expressed as a single + operand which is itself a bitmask. The extraction function always + marks it as invalid, since we never want to recognize an + instruction which uses a field of this type. */ + +static unsigned long +insert_mbe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + unsigned long uval; + int mb, me; + + uval = value; + + if (uval == 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + return insn; + } + + me = 31; + while ((uval & 1) == 0) + { + uval >>= 1; + --me; + } + + mb = me; + uval >>= 1; + while ((uval & 1) != 0) + { + uval >>= 1; + --mb; + } + + if (uval != 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + } + + return insn | (mb << 6) | (me << 1); +} + +static long +extract_mbe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + int mb, me; + int i; + + if (invalid != (int *) NULL) + *invalid = 1; + + ret = 0; + mb = (insn >> 6) & 0x1f; + me = (insn >> 1) & 0x1f; + for (i = mb; i < me; i++) + ret |= 1 << (31 - i); + return ret; +} + +/* The MB or ME field in an MD or MDS form instruction. The high bit + is wrapped to the low end. */ + +/*ARGSUSED*/ +static unsigned long +insert_mb6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 6) | (value & 0x20); +} + +/*ARGSUSED*/ +static long +extract_mb6 (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 6) & 0x1f) | (insn & 0x20); +} + +/* The NB field in an X form instruction. The value 32 is stored as + 0. */ + +static unsigned long +insert_nb (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value < 0 || value > 32) + *errmsg = "value out of range"; + if (value == 32) + value = 0; + return insn | ((value & 0x1f) << 11); +} + +/*ARGSUSED*/ +static long +extract_nb (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + + ret = (insn >> 11) & 0x1f; + if (ret == 0) + ret = 32; + return ret; +} + +/* The NSI field in a D form instruction. This is the same as the SI + field, only negated. The extraction function always marks it as + invalid, since we never want to recognize an instruction which uses + a field of this type. */ + +/*ARGSUSED*/ +static unsigned long +insert_nsi (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((- value) & 0xffff); +} + +static long +extract_nsi (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL) + *invalid = 1; + if ((insn & 0x8000) != 0) + return - ((insn & 0xffff) - 0x10000); + else + return - (insn & 0xffff); +} + +/* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ + +static unsigned long +insert_ral (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0 + || value == ((insn >> 21) & 0x1f)) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in an lmw instruction, which has special value + restrictions. */ + +static unsigned long +insert_ram (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value >= ((insn >> 21) & 0x1f)) + *errmsg = "index register in load range"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +/*ARGSUSED*/ +static unsigned long +insert_rbs (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + +static long +extract_rbs (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The SH field in an MD form instruction. This is split. */ + +/*ARGSUSED*/ +static unsigned long +insert_sh6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4); +} + +/*ARGSUSED*/ +static long +extract_sh6 (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20); +} + +/* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ + +static unsigned long +insert_spr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_spr (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); +} + +/* The TBR field in an XFX instruction. This is just like SPR, but it + is optional. When TBR is omitted, it must be inserted as 268 (the + magic number of the TB register). These functions treat 0 + (indicating an omitted optional operand) as 268. This means that + ``mftb 4,0'' is not handled correctly. This does not matter very + much, since the architecture manual does not define mftb as + accepting any values other than 268 or 269. */ + +#define TB (268) + +static unsigned long +insert_tbr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + value = TB; + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_tbr (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + + ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); + if (ret == TB) + ret = 0; + return ret; +} + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) (((x) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | (((l) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. */ +#define Y_MASK (1 << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An X form comparison instruction. */ +#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (1 << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (1 << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1)) +#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16)) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm) \ + (X ((op), (xop)) | (((fxm) & 0xff) << 12)) + +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BOF (0x4) +#define BOFP (0x5) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) +#define BOT (0xc) +#define BOTP (0xd) +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC +#define POWER PPC_OPCODE_POWER +#define POWER2 PPC_OPCODE_POWER2 +#define B32 PPC_OPCODE_32 +#define B64 PPC_OPCODE_64 +#define M601 PPC_OPCODE_601 + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +const struct powerpc_opcode powerpc_opcodes[] = { +{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdi", OP(2), OP_MASK, PPC|B64, { TO, RA, SI } }, + +{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tllti", OPTO(3,TOLLT), OPTO_MASK, POWER, { RA, SI } }, +{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPC, { RA, SI } }, +{ "teqi", OPTO(3,TOEQ), OPTO_MASK, POWER, { RA, SI } }, +{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPC, { RA, SI } }, +{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, POWER, { RA, SI } }, +{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tllei", OPTO(3,TOLLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPC, { RA, SI } }, +{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, POWER, { RA, SI } }, +{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tgti", OPTO(3,TOGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tgei", OPTO(3,TOGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twnli", OPTO(3,TONL), OPTO_MASK, PPC, { RA, SI } }, +{ "tnli", OPTO(3,TONL), OPTO_MASK, POWER, { RA, SI } }, +{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlti", OPTO(3,TOLT), OPTO_MASK, POWER, { RA, SI } }, +{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlei", OPTO(3,TOLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twngi", OPTO(3,TONG), OPTO_MASK, PPC, { RA, SI } }, +{ "tngi", OPTO(3,TONG), OPTO_MASK, POWER, { RA, SI } }, +{ "twnei", OPTO(3,TONE), OPTO_MASK, PPC, { RA, SI } }, +{ "tnei", OPTO(3,TONE), OPTO_MASK, POWER, { RA, SI } }, +{ "twi", OP(3), OP_MASK, PPC, { TO, RA, SI } }, +{ "ti", OP(3), OP_MASK, POWER, { TO, RA, SI } }, + +{ "mulli", OP(7), OP_MASK, PPC, { RT, RA, SI } }, +{ "muli", OP(7), OP_MASK, POWER, { RT, RA, SI } }, + +{ "subfic", OP(8), OP_MASK, PPC, { RT, RA, SI } }, +{ "sfi", OP(8), OP_MASK, POWER, { RT, RA, SI } }, + +{ "dozi", OP(9), OP_MASK, POWER|M601, { RT, RA, SI } }, + +{ "cmplwi", OPL(10,0), OPL_MASK, PPC, { OBF, RA, UI } }, +{ "cmpldi", OPL(10,1), OPL_MASK, PPC|B64, { OBF, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, POWER, { BF, RA, UI } }, + +{ "cmpwi", OPL(11,0), OPL_MASK, PPC, { OBF, RA, SI } }, +{ "cmpdi", OPL(11,1), OPL_MASK, PPC|B64, { OBF, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, POWER, { BF, RA, SI } }, + +{ "addic", OP(12), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai", OP(12), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic", OP(12), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "addic.", OP(13), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai.", OP(13), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic.", OP(13), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "li", OP(14), DRA_MASK, PPC, { RT, SI } }, +{ "lil", OP(14), DRA_MASK, POWER, { RT, SI } }, +{ "addi", OP(14), OP_MASK, PPC, { RT, RA, SI } }, +{ "cal", OP(14), OP_MASK, POWER, { RT, D, RA } }, +{ "subi", OP(14), OP_MASK, PPC, { RT, RA, NSI } }, +{ "la", OP(14), OP_MASK, PPC, { RT, D, RA } }, + +{ "lis", OP(15), DRA_MASK, PPC, { RT, SISIGNOPT } }, +{ "liu", OP(15), DRA_MASK, POWER, { RT, SISIGNOPT } }, +{ "addis", OP(15), OP_MASK, PPC, { RT,RA,SISIGNOPT } }, +{ "cau", OP(15), OP_MASK, POWER, { RT,RA,SISIGNOPT } }, +{ "subis", OP(15), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BD } }, +{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER, { BD } }, +{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BD } }, +{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER, { BD } }, +{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDA } }, +{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER, { BDA } }, +{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDA } }, +{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER, { BDA } }, +{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bc-", B(16,0,0), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bc+", B(16,0,0), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bc", B(16,0,0), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bcl-", B(16,0,1), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bcl+", B(16,0,1), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bcl", B(16,0,1), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bca-", B(16,1,0), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bca+", B(16,1,0), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bca", B(16,1,0), B_MASK, PPC|POWER, { BO, BI, BDA } }, +{ "bcla-", B(16,1,1), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bcla+", B(16,1,1), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bcla", B(16,1,1), B_MASK, PPC|POWER, { BO, BI, BDA } }, + +{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } }, +{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svca", SC(17,1,0), SC_MASK, POWER, { SV } }, +{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } }, + +{ "b", B(18,0,0), B_MASK, PPC|POWER, { LI } }, +{ "bl", B(18,0,1), B_MASK, PPC|POWER, { LI } }, +{ "ba", B(18,1,0), B_MASK, PPC|POWER, { LIA } }, +{ "bla", B(18,1,1), B_MASK, PPC|POWER, { LIA } }, + +{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER, { 0 } }, +{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER, { 0 } }, +{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcr", XLLK(19,16,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bcrl", XLLK(19,16,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "crnot", XL(19,33), XL_MASK, PPC, { BT, BA, BBA } }, +{ "crnor", XL(19,33), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "rfi", XL(19,50), 0xffffffff, PPC|POWER, { 0 } }, +{ "rfci", XL(19,51), 0xffffffff, PPC, { 0 } }, + +{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } }, + +{ "crandc", XL(19,129), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "isync", XL(19,150), 0xffffffff, PPC, { 0 } }, +{ "ics", XL(19,150), 0xffffffff, POWER, { 0 } }, + +{ "crclr", XL(19,193), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "crxor", XL(19,193), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crnand", XL(19,225), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crand", XL(19,257), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crset", XL(19,289), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "creqv", XL(19,289), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crorc", XL(19,417), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crmove", XL(19,449), XL_MASK, PPC, { BT, BA, BBA } }, +{ "cror", XL(19,449), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcc", XLLK(19,528,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bccl", XLLK(19,528,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "rlwimi", M(20,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi", M(20,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlwimi.", M(20,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi.", M(20,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rotlwi", MME(21,31,0), MMBME_MASK, PPC, { RA, RS, SH } }, +{ "clrlwi", MME(21,31,0), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm", M(21,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm", M(21,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, +{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPC, { RA,RS,SH } }, +{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm.", M(21,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm.", M(21,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlmi", M(22,0), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, +{ "rlmi.", M(22,1), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, + +{ "rotlw", MME(23,31,0), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm", M(23,0), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm", M(23,0), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, +{ "rotlw.", MME(23,31,1), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm.", M(23,1), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm.", M(23,1), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, + +{ "nop", OP(24), 0xffffffff, PPC, { 0 } }, +{ "ori", OP(24), OP_MASK, PPC, { RA, RS, UI } }, +{ "oril", OP(24), OP_MASK, POWER, { RA, RS, UI } }, + +{ "oris", OP(25), OP_MASK, PPC, { RA, RS, UI } }, +{ "oriu", OP(25), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xori", OP(26), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoril", OP(26), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xoris", OP(27), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoriu", OP(27), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andi.", OP(28), OP_MASK, PPC, { RA, RS, UI } }, +{ "andil.", OP(28), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andis.", OP(29), OP_MASK, PPC, { RA, RS, UI } }, +{ "andiu.", OP(29), OP_MASK, POWER, { RA, RS, UI } }, + +{ "rotldi", MD(30,0,0), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi", MD(30,0,0), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl", MD(30,0,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl.", MD(30,0,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldicr", MD(30,1,0), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, +{ "rldicr.", MD(30,1,1), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, + +{ "rldic", MD(30,2,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldic.", MD(30,2,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldimi", MD(30,3,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldimi.", MD(30,3,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl", MDS(30,8,0), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, +{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, + +{ "rldcr", MDS(30,9,0), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, +{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, + +{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmp", X(31,0), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPC, { RA, RB } }, +{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, POWER, { RA, RB } }, +{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPC, { RA, RB } }, +{ "tllt", XTO(31,4,TOLLT), XTO_MASK, POWER, { RA, RB } }, +{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPC, { RA, RB } }, +{ "teq", XTO(31,4,TOEQ), XTO_MASK, POWER, { RA, RB } }, +{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPC, { RA, RB } }, +{ "tlge", XTO(31,4,TOLGE), XTO_MASK, POWER, { RA, RB } }, +{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPC, { RA, RB } }, +{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, POWER, { RA, RB } }, +{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPC, { RA, RB } }, +{ "tlle", XTO(31,4,TOLLE), XTO_MASK, POWER, { RA, RB } }, +{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPC, { RA, RB } }, +{ "tlng", XTO(31,4,TOLNG), XTO_MASK, POWER, { RA, RB } }, +{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPC, { RA, RB } }, +{ "tgt", XTO(31,4,TOGT), XTO_MASK, POWER, { RA, RB } }, +{ "twge", XTO(31,4,TOGE), XTO_MASK, PPC, { RA, RB } }, +{ "tge", XTO(31,4,TOGE), XTO_MASK, POWER, { RA, RB } }, +{ "twnl", XTO(31,4,TONL), XTO_MASK, PPC, { RA, RB } }, +{ "tnl", XTO(31,4,TONL), XTO_MASK, POWER, { RA, RB } }, +{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPC, { RA, RB } }, +{ "tlt", XTO(31,4,TOLT), XTO_MASK, POWER, { RA, RB } }, +{ "twle", XTO(31,4,TOLE), XTO_MASK, PPC, { RA, RB } }, +{ "tle", XTO(31,4,TOLE), XTO_MASK, POWER, { RA, RB } }, +{ "twng", XTO(31,4,TONG), XTO_MASK, PPC, { RA, RB } }, +{ "tng", XTO(31,4,TONG), XTO_MASK, POWER, { RA, RB } }, +{ "twne", XTO(31,4,TONE), XTO_MASK, PPC, { RA, RB } }, +{ "tne", XTO(31,4,TONE), XTO_MASK, POWER, { RA, RB } }, +{ "trap", XTO(31,4,TOU), 0xffffffff, PPC, { 0 } }, +{ "tw", X(31,4), X_MASK, PPC, { TO, RA, RB } }, +{ "t", X(31,4), X_MASK, POWER, { TO, RA, RB } }, + +{ "subfc", XO(31,8,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf", XO(31,8,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf.", XO(31,8,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco", XO(31,8,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo", XO(31,8,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo.", XO(31,8,1,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addc", XO(31,10,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "a", XO(31,10,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addc.", XO(31,10,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "a.", XO(31,10,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco", XO(31,10,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao", XO(31,10,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco.", XO(31,10,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao.", XO(31,10,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfcr", X(31,19), XRARB_MASK, POWER|PPC, { RT } }, + +{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } }, + +{ "ldx", X(31,21), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lwzx", X(31,23), X_MASK, PPC, { RT, RA, RB } }, +{ "lx", X(31,23), X_MASK, POWER, { RT, RA, RB } }, + +{ "slw", XRC(31,24,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sl", XRC(31,24,0), X_MASK, POWER, { RA, RS, RB } }, +{ "slw.", XRC(31,24,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sl.", XRC(31,24,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "cntlzw", XRC(31,26,0), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz", XRC(31,26,0), XRB_MASK, POWER, { RA, RS } }, +{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz.", XRC(31,26,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sld", XRC(31,27,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "sld.", XRC(31,27,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "and", XRC(31,28,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "and.", XRC(31,28,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "maskg", XRC(31,29,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskg.", XRC(31,29,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmpl", X(31,32), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "ldux", X(31,53), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } }, + +{ "lwzux", X(31,55), X_MASK, PPC, { RT, RAL, RB } }, +{ "lux", X(31,55), X_MASK, POWER, { RT, RA, RB } }, + +{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC|B64, { RA, RS } }, +{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC|B64, { RA, RS } }, + +{ "andc", XRC(31,60,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "andc.", XRC(31,60,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "td", X(31,68), X_MASK, PPC|B64, { TO, RA, RB } }, + +{ "mulhd", XO(31,73,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfmsr", X(31,83), XRARB_MASK, PPC|POWER, { RT } }, + +{ "ldarx", X(31,84), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } }, + +{ "lbzx", X(31,87), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "neg", XO(31,104,0,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "neg.", XO(31,104,0,1), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego", XO(31,104,1,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego.", XO(31,104,1,1), XORB_MASK, PPC|POWER, { RT, RA } }, + +{ "mul", XO(31,107,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mul.", XO(31,107,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo", XO(31,107,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo.", XO(31,107,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } }, + +{ "lbzux", X(31,119), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "not", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "not.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "subfe", XO(31,136,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe", XO(31,136,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfe.", XO(31,136,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe.", XO(31,136,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo", XO(31,136,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo", XO(31,136,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo.", XO(31,136,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo.", XO(31,136,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "adde", XO(31,138,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae", XO(31,138,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "adde.", XO(31,138,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae.", XO(31,138,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo", XO(31,138,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo", XO(31,138,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo.", XO(31,138,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo.", XO(31,138,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }}, +{ "mtcrf", X(31,144), XFXFXM_MASK, PPC|POWER, { FXM, RS } }, + +{ "mtmsr", X(31,146), XRARB_MASK, PPC|POWER, { RS } }, + +{ "stdx", X(31,149), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } }, + +{ "stwx", X(31,151), X_MASK, PPC, { RS, RA, RB } }, +{ "stx", X(31,151), X_MASK, POWER, { RS, RA, RB } }, + +{ "slq", XRC(31,152,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "slq.", XRC(31,152,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sle", XRC(31,153,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sle.", XRC(31,153,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stdux", X(31,181), X_MASK, PPC|B64, { RS, RAS, RB } }, + +{ "stwux", X(31,183), X_MASK, PPC, { RS, RAS, RB } }, +{ "stux", X(31,183), X_MASK, POWER, { RS, RA, RB } }, + +{ "sliq", XRC(31,184,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sliq.", XRC(31,184,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "subfze", XO(31,200,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfze", XO(31,200,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfze.", XO(31,200,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo", XO(31,200,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo.", XO(31,200,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "addze", XO(31,202,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "aze", XO(31,202,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addze.", XO(31,202,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "aze.", XO(31,202,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo", XO(31,202,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "azeo", XO(31,202,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "azeo.", XO(31,202,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mtsr", X(31,210), XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } }, + +{ "stdcx.", XRC(31,214,1), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stbx", X(31,215), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sllq", XRC(31,216,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sllq.", XRC(31,216,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sleq", XRC(31,217,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sleq.", XRC(31,217,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "subfme", XO(31,232,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfme", XO(31,232,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfme.", XO(31,232,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo", XO(31,232,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo.", XO(31,232,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mulld", XO(31,233,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulld.", XO(31,233,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo", XO(31,233,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addme", XO(31,234,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "ame", XO(31,234,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addme.", XO(31,234,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "ame.", XO(31,234,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo", XO(31,234,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "ameo", XO(31,234,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "ameo.", XO(31,234,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mullw", XO(31,235,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls", XO(31,235,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullw.", XO(31,235,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls.", XO(31,235,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo", XO(31,235,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso", XO(31,235,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo.", XO(31,235,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso.", XO(31,235,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtsrin", X(31,242), XRA_MASK, PPC|B32, { RS, RB } }, +{ "mtsri", X(31,242), XRA_MASK, POWER|B32, { RS, RB } }, + +{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } }, + +{ "stbux", X(31,247), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "slliq", XRC(31,248,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "slliq.", XRC(31,248,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "doz", XO(31,264,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "doz.", XO(31,264,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo", XO(31,264,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo.", XO(31,264,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "add", XO(31,266,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax", XO(31,266,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "add.", XO(31,266,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax.", XO(31,266,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo", XO(31,266,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo", XO(31,266,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo.", XO(31,266,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo.", XO(31,266,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "lscbx", XRC(31,277,0), X_MASK, POWER|M601, { RT, RA, RB } }, +{ "lscbx.", XRC(31,277,1), X_MASK, POWER|M601, { RT, RA, RB } }, + +{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } }, + +{ "lhzx", X(31,279), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "icbt", X(31,262), XRT_MASK, PPC, { RA, RB } }, + +{ "eqv", XRC(31,284,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "eqv.", XRC(31,284,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } }, +{ "tlbi", X(31,306), XRTRA_MASK, POWER, { RB } }, + +{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } }, + +{ "lhzux", X(31,311), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "xor", XRC(31,316,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "xor.", XRC(31,316,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mfdcr", X(31,323), X_MASK, PPC, { RT, SPR } }, + +{ "div", XO(31,331,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "div.", XO(31,331,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo", XO(31,331,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo.", XO(31,331,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "mfmq", XSPR(31,339,0), XSPR_MASK, POWER|M601, { RT } }, +{ "mfxer", XSPR(31,339,1), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,6), XSPR_MASK, POWER|M601, { RT } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfctr", XSPR(31,339,9), XSPR_MASK, PPC|POWER, { RT } }, +{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } }, +{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdar", XSPR(31,339,19), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,22), XSPR_MASK, PPC, { RT } }, +{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } }, +{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } }, +{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC|B64, { RT } }, +{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } }, +{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } }, +{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfspr", X(31,339), X_MASK, PPC|POWER, { RT, SPR } }, + +{ "lwax", X(31,341), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lhax", X(31,343), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "dccci", X(31,454), XRT_MASK, PPC, { RA, RB } }, + +{ "abs", XO(31,360,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abs.", XO(31,360,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso", XO(31,360,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso.", XO(31,360,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divs", XO(31,363,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divs.", XO(31,363,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso", XO(31,363,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso.", XO(31,363,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } }, + +{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } }, +{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } }, + +{ "lwaux", X(31,373), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "lhaux", X(31,375), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "sthx", X(31,407), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "orc", XRC(31,412,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "orc.", XRC(31,412,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "sradi", XS(31,413,0), XS_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "sradi.", XS(31,413,1), XS_MASK, PPC|B64, { RA, RS, SH6 } }, + +{ "slbie", X(31,434), XRTRA_MASK, PPC|B64, { RB } }, + +{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } }, + +{ "sthux", X(31,439), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "mr", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "mr.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mtdcr", X(31,451), X_MASK, PPC, { SPR, RS } }, + +{ "divdu", XO(31,457,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdu.", XO(31,457,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo", XO(31,457,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo.", XO(31,457,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtmq", XSPR(31,467,0), XSPR_MASK, POWER|M601, { RS } }, +{ "mtxer", XSPR(31,467,1), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, PPC|POWER, { RS } }, +{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } }, +{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdar", XSPR(31,467,19), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdec", XSPR(31,467,22), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } }, +{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } }, +{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC|B64, { RS } }, +{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } }, +{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } }, +{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } }, +{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtspr", X(31,467), X_MASK, PPC|POWER, { SPR, RS } }, + +{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } }, + +{ "nand", XRC(31,476,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "nand.", XRC(31,476,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "nabs", XO(31,488,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabs.", XO(31,488,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso", XO(31,488,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso.", XO(31,488,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divd", XO(31,489,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divd.", XO(31,489,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo", XO(31,489,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo.", XO(31,489,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "slbia", X(31,498), 0xffffffff, PPC|B64, { 0 } }, + +{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } }, + +{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), PPC|POWER, { BF } }, + +{ "clcs", X(31,531), XRB_MASK, POWER|M601, { RT, RA } }, + +{ "lswx", X(31,533), X_MASK, PPC, { RT, RA, RB } }, +{ "lsx", X(31,533), X_MASK, POWER, { RT, RA, RB } }, + +{ "lwbrx", X(31,534), X_MASK, PPC, { RT, RA, RB } }, +{ "lbrx", X(31,534), X_MASK, POWER, { RT, RA, RB } }, + +{ "lfsx", X(31,535), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "srw", XRC(31,536,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sr", XRC(31,536,0), X_MASK, POWER, { RA, RS, RB } }, +{ "srw.", XRC(31,536,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sr.", XRC(31,536,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "rrib", XRC(31,537,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "rrib.", XRC(31,537,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srd", XRC(31,539,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srd.", XRC(31,539,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "maskir", XRC(31,541,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskir.", XRC(31,541,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } }, + +{ "lfsux", X(31,567), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsr", X(31,595), XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } }, + +{ "lswi", X(31,597), X_MASK, PPC, { RT, RA, NB } }, +{ "lsi", X(31,597), X_MASK, POWER, { RT, RA, NB } }, + +{ "sync", X(31,598), 0xffffffff, PPC, { 0 } }, +{ "dcs", X(31,598), 0xffffffff, POWER, { 0 } }, + +{ "lfdx", X(31,599), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "mfsri", X(31,627), X_MASK, POWER, { RT, RA, RB } }, + +{ "dclst", X(31,630), XRB_MASK, POWER, { RS, RA } }, + +{ "lfdux", X(31,631), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsrin", X(31,659), XRA_MASK, PPC|B32, { RT, RB } }, + +{ "stswx", X(31,661), X_MASK, PPC, { RS, RA, RB } }, +{ "stsx", X(31,661), X_MASK, POWER, { RS, RA, RB } }, + +{ "stwbrx", X(31,662), X_MASK, PPC, { RS, RA, RB } }, +{ "stbrx", X(31,662), X_MASK, POWER, { RS, RA, RB } }, + +{ "stfsx", X(31,663), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srq", XRC(31,664,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srq.", XRC(31,664,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sre", XRC(31,665,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sre.", XRC(31,665,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfsux", X(31,695), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "sriq", XRC(31,696,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sriq.", XRC(31,696,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "stswi", X(31,725), X_MASK, PPC, { RS, RA, NB } }, +{ "stsi", X(31,725), X_MASK, POWER, { RS, RA, NB } }, + +{ "stfdx", X(31,727), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srlq", XRC(31,728,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srlq.", XRC(31,728,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sreq", XRC(31,729,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sreq.", XRC(31,729,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfdux", X(31,759), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "srliq", XRC(31,760,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "srliq.", XRC(31,760,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "lhbrx", X(31,790), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "sraw", XRC(31,792,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sra", XRC(31,792,0), X_MASK, POWER, { RA, RS, RB } }, +{ "sraw.", XRC(31,792,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sra.", XRC(31,792,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "srad", XRC(31,794,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srad.", XRC(31,794,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "rac", X(31,818), X_MASK, POWER, { RT, RA, RB } }, + +{ "srawi", XRC(31,824,0), X_MASK, PPC, { RA, RS, SH } }, +{ "srai", XRC(31,824,0), X_MASK, POWER, { RA, RS, SH } }, +{ "srawi.", XRC(31,824,1), X_MASK, PPC, { RA, RS, SH } }, +{ "srai.", XRC(31,824,1), X_MASK, POWER, { RA, RS, SH } }, + +{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } }, + +{ "sthbrx", X(31,918), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sraq", XRC(31,920,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sraq.", XRC(31,920,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srea", XRC(31,921,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srea.", XRC(31,921,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "extsh", XRC(31,922,0), XRB_MASK, PPC, { RA, RS } }, +{ "exts", XRC(31,922,0), XRB_MASK, POWER, { RA, RS } }, +{ "extsh.", XRC(31,922,1), XRB_MASK, PPC, { RA, RS } }, +{ "exts.", XRC(31,922,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sraiq", XRC(31,952,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sraiq.", XRC(31,952,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, +{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} }, + +{ "iccci", X(31,966), XRT_MASK, PPC, { RA, RB } }, + +{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } }, + +{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } }, + +{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } }, +{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } }, + +{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, +{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, + +{ "lwz", OP(32), OP_MASK, PPC, { RT, D, RA } }, +{ "l", OP(32), OP_MASK, POWER, { RT, D, RA } }, + +{ "lwzu", OP(33), OP_MASK, PPC, { RT, D, RAL } }, +{ "lu", OP(33), OP_MASK, POWER, { RT, D, RA } }, + +{ "lbz", OP(34), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lbzu", OP(35), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "stw", OP(36), OP_MASK, PPC, { RS, D, RA } }, +{ "st", OP(36), OP_MASK, POWER, { RS, D, RA } }, + +{ "stwu", OP(37), OP_MASK, PPC, { RS, D, RAS } }, +{ "stu", OP(37), OP_MASK, POWER, { RS, D, RA } }, + +{ "stb", OP(38), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "stbu", OP(39), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lhz", OP(40), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhzu", OP(41), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "lha", OP(42), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhau", OP(43), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "sth", OP(44), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "sthu", OP(45), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lmw", OP(46), OP_MASK, PPC, { RT, D, RAM } }, +{ "lm", OP(46), OP_MASK, POWER, { RT, D, RA } }, + +{ "stmw", OP(47), OP_MASK, PPC, { RS, D, RA } }, +{ "stm", OP(47), OP_MASK, POWER, { RS, D, RA } }, + +{ "lfs", OP(48), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfsu", OP(49), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "lfd", OP(50), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfdu", OP(51), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "stfs", OP(52), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfsu", OP(53), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "stfd", OP(54), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfdu", OP(55), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "ld", DSO(58,0), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "ldu", DSO(58,1), DS_MASK, PPC|B64, { RT, DS, RAL } }, + +{ "lwa", DSO(58,2), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, + +{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "std", DSO(62,0), DS_MASK, PPC|B64, { RS, DS, RA } }, + +{ "stdu", DSO(62,1), DS_MASK, PPC|B64, { RS, DS, RAS } }, + +{ "fcmpu", X(63,0), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "frsp", XRC(63,12,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "frsp.", XRC(63,12,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fctiw", XRC(63,14,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiw.", XRC(63,14,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fdiv", A(63,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd", A(63,18,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fdiv.", A(63,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd.", A(63,18,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsub", A(63,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs", A(63,20,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fsub.", A(63,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs.", A(63,20,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fadd", A(63,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa", A(63,21,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fadd.", A(63,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa.", A(63,21,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, +{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, + +{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmul", A(63,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm", A(63,25,0), AFRB_MASK, POWER, { FRT, FRA, FRC } }, +{ "fmul.", A(63,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm.", A(63,25,1), AFRB_MASK, POWER, { FRT, FRA, FRC } }, + +{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmsub", A(63,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms", A(63,28,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmsub.", A(63,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms.", A(63,28,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fmadd", A(63,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma", A(63,29,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmadd.", A(63,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma.", A(63,29,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmsub", A(63,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms", A(63,30,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmsub.", A(63,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms.", A(63,30,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmadd", A(63,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma", A(63,31,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmadd.", A(63,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma.", A(63,31,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fcmpo", X(63,30), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "mtfsb1", XRC(63,38,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fneg", XRC(63,40,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fneg.", XRC(63,40,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "mtfsb0", XRC(63,70,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fmr", XRC(63,72,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fmr.", XRC(63,72,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, +{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, + +{ "fnabs", XRC(63,136,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fnabs.", XRC(63,136,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fabs", XRC(63,264,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fabs.", XRC(63,264,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mffs", XRC(63,583,0), XRARB_MASK, PPC|POWER, { FRT } }, +{ "mffs.", XRC(63,583,1), XRARB_MASK, PPC|POWER, { FRT } }, + +{ "mtfsf", XFL(63,711,0), XFL_MASK, PPC|POWER, { FLM, FRB } }, +{ "mtfsf.", XFL(63,711,1), XFL_MASK, PPC|POWER, { FLM, FRB } }, + +{ "fctid", XRC(63,814,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctid.", XRC(63,814,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fctidz", XRC(63,815,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fcfid", XRC(63,846,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +}; + +const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); + +/* The macro table. This is only used by the assembler. */ + +const struct powerpc_macro powerpc_macros[] = { +{ "extldi", 4, PPC|B64, "rldicr %0,%1,%3,(%2)-1" }, +{ "extldi.", 4, PPC|B64, "rldicr. %0,%1,%3,(%2)-1" }, +{ "extrdi", 4, PPC|B64, "rldicl %0,%1,(%2)+(%3),64-(%2)" }, +{ "extrdi.", 4, PPC|B64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" }, +{ "insrdi", 4, PPC|B64, "rldimi %0,%1,64-((%2)+(%3)),%3" }, +{ "insrdi.", 4, PPC|B64, "rldimi. %0,%1,64-((%2)+(%3)),%3" }, +{ "rotrdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),0" }, +{ "rotrdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),0" }, +{ "sldi", 3, PPC|B64, "rldicr %0,%1,%2,63-(%2)" }, +{ "sldi.", 3, PPC|B64, "rldicr. %0,%1,%2,63-(%2)" }, +{ "srdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),%2" }, +{ "srdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),%2" }, +{ "clrrdi", 3, PPC|B64, "rldicr %0,%1,0,63-(%2)" }, +{ "clrrdi.", 3, PPC|B64, "rldicr. %0,%1,0,63-(%2)" }, +{ "clrlsldi",4, PPC|B64, "rldic %0,%1,%3,(%2)-(%3)" }, +{ "clrlsldi.",4, PPC|B64, "rldic. %0,%1,%3,(%2)-(%3)" }, + +{ "extlwi", 4, PPC, "rlwinm %0,%1,%3,0,(%2)-1" }, +{ "extlwi.", 4, PPC, "rlwinm. %0,%1,%3,0,(%2)-1" }, +{ "extrwi", 4, PPC, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" }, +{ "extrwi.", 4, PPC, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" }, +{ "inslwi", 4, PPC, "rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "inslwi.", 4, PPC, "rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "insrwi", 4, PPC, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" }, +{ "insrwi.", 4, PPC, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"}, +{ "rotrwi", 3, PPC, "rlwinm %0,%1,32-(%2),0,31" }, +{ "rotrwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),0,31" }, +{ "slwi", 3, PPC, "rlwinm %0,%1,%2,0,31-(%2)" }, +{ "sli", 3, POWER, "rlinm %0,%1,%2,0,31-(%2)" }, +{ "slwi.", 3, PPC, "rlwinm. %0,%1,%2,0,31-(%2)" }, +{ "sli.", 3, POWER, "rlinm. %0,%1,%2,0,31-(%2)" }, +{ "srwi", 3, PPC, "rlwinm %0,%1,32-(%2),%2,31" }, +{ "sri", 3, POWER, "rlinm %0,%1,32-(%2),%2,31" }, +{ "srwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),%2,31" }, +{ "sri.", 3, POWER, "rlinm. %0,%1,32-(%2),%2,31" }, +{ "clrrwi", 3, PPC, "rlwinm %0,%1,0,0,31-(%2)" }, +{ "clrrwi.", 3, PPC, "rlwinm. %0,%1,0,0,31-(%2)" }, +{ "clrlslwi",4, PPC, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" }, +{ "clrlslwi.",4, PPC, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" }, + +}; + +const int powerpc_num_macros = + sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/ppc.h linux/arch/ppc/xmon/ppc.h --- v2.2.11/linux/arch/ppc/xmon/ppc.h Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/ppc.h Wed Aug 25 17:29:46 1999 @@ -0,0 +1,240 @@ +/* ppc.h -- Header file for PowerPC opcode table + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +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 file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PPC_H +#define PPC_H + +/* The opcode table is an array of struct powerpc_opcode. */ + +struct powerpc_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned long opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned long mask; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The defined values + are listed below. */ + unsigned long flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[8]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct powerpc_opcode powerpc_opcodes[]; +extern const int powerpc_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* Opcode is defined for the PowerPC architecture. */ +#define PPC_OPCODE_PPC (01) + +/* Opcode is defined for the POWER (RS/6000) architecture. */ +#define PPC_OPCODE_POWER (02) + +/* Opcode is defined for the POWER2 (Rios 2) architecture. */ +#define PPC_OPCODE_POWER2 (04) + +/* Opcode is only defined on 32 bit architectures. */ +#define PPC_OPCODE_32 (010) + +/* Opcode is only defined on 64 bit architectures. */ +#define PPC_OPCODE_64 (020) + +/* Opcode is supported by the Motorola PowerPC 601 processor. The 601 + is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions, + but it also supports many additional POWER instructions. */ +#define PPC_OPCODE_601 (040) + +/* A macro to extract the major opcode from an instruction. */ +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +/* The operands table is an array of struct powerpc_operand. */ + +struct powerpc_operand +{ + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned long (*insert) PARAMS ((unsigned long instruction, long op, + const char **errmsg)); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & PPC_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + long (*extract) PARAMS ((unsigned long instruction, int *invalid)); + + /* One bit syntax flags. */ + unsigned long flags; +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +extern const struct powerpc_operand powerpc_operands[]; + +/* Values defined for the flags field of a struct powerpc_operand. */ + +/* This operand takes signed values. */ +#define PPC_OPERAND_SIGNED (01) + +/* This operand takes signed values, but also accepts a full positive + range of values when running in 32 bit mode. That is, if bits is + 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode, + this flag is ignored. */ +#define PPC_OPERAND_SIGNOPT (02) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics such as mr, for which two + operands fields are identical. The assembler should call the + insert function with any op value. The disassembler should call + the extract function, ignore the return value, and check the value + placed in the valid argument. */ +#define PPC_OPERAND_FAKE (04) + +/* The next operand should be wrapped in parentheses rather than + separated from this one by a comma. This is used for the load and + store instructions which want their operands to look like + reg,displacement(reg) + */ +#define PPC_OPERAND_PARENS (010) + +/* This operand may use the symbolic names for the CR fields, which + are + lt 0 gt 1 eq 2 so 3 un 3 + cr0 0 cr1 1 cr2 2 cr3 3 + cr4 4 cr5 5 cr6 6 cr7 7 + These may be combined arithmetically, as in cr2*4+gt. These are + only supported on the PowerPC, not the POWER. */ +#define PPC_OPERAND_CR (020) + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define PPC_OPERAND_GPR (040) + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define PPC_OPERAND_FPR (0100) + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_RELATIVE (0200) + +/* This operand is an absolute branch address. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_ABSOLUTE (0400) + +/* This operand is optional, and is zero if omitted. This is used for + the optional BF and L fields in the comparison instructions. The + assembler must count the number of operands remaining on the line, + and the number of operands remaining for the opcode, and decide + whether this operand is present or not. The disassembler should + print this operand out only if it is not zero. */ +#define PPC_OPERAND_OPTIONAL (01000) + +/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand + is omitted, then for the next operand use this operand value plus + 1, ignoring the next operand field for the opcode. This wretched + hack is needed because the Power rotate instructions can take + either 4 or 5 operands. The disassembler should print this operand + out regardless of the PPC_OPERAND_OPTIONAL field. */ +#define PPC_OPERAND_NEXT (02000) + +/* This operand should be regarded as a negative number for the + purposes of overflow checking (i.e., the normal most negative + number is disallowed and one more than the normal most positive + number is allowed). This flag will only be set for a signed + operand. */ +#define PPC_OPERAND_NEGATIVE (04000) + +/* The POWER and PowerPC assemblers use a few macros. We keep them + with the operands table for simplicity. The macro table is an + array of struct powerpc_macro. */ + +struct powerpc_macro +{ + /* The macro name. */ + const char *name; + + /* The number of operands the macro takes. */ + unsigned int operands; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The values are the + same as those for the struct powerpc_opcode flags field. */ + unsigned long flags; + + /* A format string to turn the macro into a normal instruction. + Each %N in the string is replaced with operand number N (zero + based). */ + const char *format; +}; + +extern const struct powerpc_macro powerpc_macros[]; +extern const int powerpc_num_macros; + +#endif /* PPC_H */ diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/privinst.h linux/arch/ppc/xmon/privinst.h --- v2.2.11/linux/arch/ppc/xmon/privinst.h Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/privinst.h Wed Aug 25 17:29:46 1999 @@ -0,0 +1,73 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ + +#define GETREG(reg) \ + static inline int get_ ## reg (void) \ + { int ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; } + +#define SETREG(reg) \ + static inline void set_ ## reg (int val) \ + { asm volatile ("mt" #reg " %0" : : "r" (val)); } + +GETREG(msr) +SETREG(msr) +GETREG(cr) + +#define GSETSPR(n, name) \ + static inline int get_ ## name (void) \ + { int ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \ + static inline void set_ ## name (int val) \ + { asm volatile ("mtspr " #n ",%0" : : "r" (val)); } + +GSETSPR(0, mq) +GSETSPR(1, xer) +GSETSPR(4, rtcu) +GSETSPR(5, rtcl) +GSETSPR(8, lr) +GSETSPR(9, ctr) +GSETSPR(18, dsisr) +GSETSPR(19, dar) +GSETSPR(22, dec) +GSETSPR(25, sdr1) +GSETSPR(26, srr0) +GSETSPR(27, srr1) +GSETSPR(272, sprg0) +GSETSPR(273, sprg1) +GSETSPR(274, sprg2) +GSETSPR(275, sprg3) +GSETSPR(282, ear) +GSETSPR(287, pvr) +GSETSPR(528, bat0u) +GSETSPR(529, bat0l) +GSETSPR(530, bat1u) +GSETSPR(531, bat1l) +GSETSPR(532, bat2u) +GSETSPR(533, bat2l) +GSETSPR(534, bat3u) +GSETSPR(535, bat3l) +GSETSPR(1008, hid0) +GSETSPR(1009, hid1) +GSETSPR(1010, iabr) +GSETSPR(1013, dabr) +GSETSPR(1023, pir) + +static inline int get_sr(int n) +{ + int ret; + + asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28)); + return ret; +} + +static inline void set_sr(int n, int val) +{ + asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28)); +} + +static inline void store_inst(void *p) +{ + asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p)); +} + + diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/setjmp.c linux/arch/ppc/xmon/setjmp.c --- v2.2.11/linux/arch/ppc/xmon/setjmp.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/setjmp.c Wed Aug 25 17:29:46 1999 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * + * NB this file must be compiled with -O2. + */ + +int +xmon_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} + +void +xmon_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/start.c linux/arch/ppc/xmon/start.c --- v2.2.11/linux/arch/ppc/xmon/start.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/start.c Wed Aug 25 17:29:46 1999 @@ -0,0 +1,392 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static volatile unsigned char *sccc, *sccd; +unsigned long TXRDY, RXRDY; +extern void xmon_printf(const char *fmt, ...); +extern void map_bootx_text(void); +extern void drawchar(char); +extern void drawstring(const char *str); + +static int console = 0; +static int use_screen = 0; + +void buf_access(void) +{ + if ( _machine == _MACH_chrp ) + sccd[3] &= ~0x80; /* reset DLAB */ +} + +void +xmon_map_scc(void) +{ + volatile unsigned char *base; + + if ( _machine == _MACH_Pmac ) + { + struct device_node *np; + extern boot_infos_t *boot_infos; + unsigned long addr; + +#ifdef CONFIG_BOOTX_TEXT + if (boot_infos != 0 && find_via_pmu()) { + printk(KERN_INFO "xmon uses screen and keyboard\n"); + use_screen = 1; + map_bootx_text(); + return; + } +#endif +#ifdef CHRP_ESCC + addr = 0xc1013020; +#else + addr = 0xf3013020; +#endif + TXRDY = 4; + RXRDY = 1; + + np = find_devices("mac-io"); + if (np && np->n_addrs) { + addr = np->addrs[0].address + 0x13000; + /* use the B channel on the iMac, A channel on others */ + if (addr >= 0xf0000000) + addr += 0x20; /* use A channel */ + } + base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE); + sccc = base + (addr & ~PAGE_MASK); +#ifdef CHRP_ESCC + sccd = sccc + (0xc1013030 - 0xc1013020); +#else + sccd = sccc + (0xf3013030 - 0xf3013020); +#endif + } + else + { + /* should already be mapped by the kernel boot */ + sccc = (volatile unsigned char *) (isa_io_base + 0x3fd); + sccd = (volatile unsigned char *) (isa_io_base + 0x3f8); + TXRDY = 0x20; + RXRDY = 1; + } +} + +static int scc_initialized = 0; + +void xmon_init_scc(void); +extern void pmu_poll(void); + +int +xmon_write(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, ct; + +#ifdef CONFIG_BOOTX_TEXT + if (use_screen) { + /* write it on the screen */ + for (i = 0; i < nb; ++i) + drawchar(*p++); + return nb; + } +#endif + if (!scc_initialized) + xmon_init_scc(); + for (i = 0; i < nb; ++i) { + while ((*sccc & TXRDY) == 0) + if (adb_hardware == ADB_VIAPMU) + pmu_poll(); + buf_access(); + if ( console && (*p != '\r')) + printk("%c", *p); + ct = 0; + if ( *p == '\n') + ct = 1; + *sccd = *p++; + if ( ct ) + xmon_write(handle, "\r", 1); + } + return i; +} + +int xmon_wants_key; +int xmon_pmu_keycode; + +#ifdef CONFIG_BOOTX_TEXT +static int xmon_pmu_shiftstate; + +static unsigned char xmon_keytab[128] = + "asdfhgzxcv\000bqwer" /* 0x00 - 0x0f */ + "yt123465=97-80o]" /* 0x10 - 0x1f */ + "u[ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */ + "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ + "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ + "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ + +static unsigned char xmon_shift_keytab[128] = + "ASDFHGZXCV\000BQWER" /* 0x00 - 0x0f */ + "YT!@#$^%+(&=*)}O" /* 0x10 - 0x1f */ + "U{IP\rLJ\"K:|" /* 0x20 - 0x2f */ + "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */ + "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */ + "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */ + +static int +xmon_get_pmu_key(void) +{ + int k, t, on; + + xmon_wants_key = 1; + for (;;) { + xmon_pmu_keycode = -1; + t = 0; + on = 0; + do { + if (--t < 0) { + on = 1 - on; + drawchar(on? 0xdb: 0x20); + drawchar('\b'); + t = 200000; + } + pmu_poll(); + } while (xmon_pmu_keycode == -1); + k = xmon_pmu_keycode; + if (on) + drawstring(" \b"); + + /* test for shift keys */ + if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) { + xmon_pmu_shiftstate = (k & 0x80) == 0; + continue; + } + if (k >= 0x80) + continue; /* ignore up transitions */ + k = (xmon_pmu_shiftstate? xmon_shift_keytab: xmon_keytab)[k]; + if (k != 0) + break; + } + xmon_wants_key = 0; + return k; +} +#endif /* CONFIG_BOOTX_TEXT */ + +int +xmon_read(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i; + +#ifdef CONFIG_BOOTX_TEXT + if (use_screen) { + for (i = 0; i < nb; ++i) + *p++ = xmon_get_pmu_key(); + return i; + } +#endif + if (!scc_initialized) + xmon_init_scc(); + for (i = 0; i < nb; ++i) { + while ((*sccc & RXRDY) == 0) + if (adb_hardware == ADB_VIAPMU) + pmu_poll(); + buf_access(); +#if 0 + if ( 0/*console*/ ) + *p++ = ppc_md.kbd_getkeycode(); + else +#endif + *p++ = *sccd; + } + return i; +} + +static unsigned char scc_inittab[] = { + 13, 0, /* set baud rate divisor */ + 12, 1, + 14, 1, /* baud rate gen enable, src=rtxc */ + 11, 0x50, /* clocks = br gen */ + 5, 0x6a, /* tx 8 bits, assert RTS */ + 4, 0x44, /* x16 clock, 1 stop */ + 3, 0xc1, /* rx enable, 8 bits */ +}; + +void +xmon_init_scc() +{ + if ( _machine == _MACH_chrp ) + { + sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ + sccd[0] = 3; eieio(); /* DLL = 38400 baud */ + sccd[1] = 0; eieio(); + sccd[2] = 0; eieio(); /* FCR = 0 */ + sccd[3] = 3; eieio(); /* LCR = 8N1 */ + sccd[1] = 0; eieio(); /* IER = 0 */ + } + else + { + int i, x; + + for (i = 20000; i != 0; --i) { + x = *sccc; eieio(); + } + *sccc = 9; eieio(); /* reset A or B side */ + *sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio(); + for (i = 0; i < sizeof(scc_inittab); ++i) { + *sccc = scc_inittab[i]; + eieio(); + } + } + scc_initialized = 1; +} + +#if 0 +extern int (*prom_entry)(void *); + +int +xmon_exit(void) +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*prom_entry)(&args); + } +} +#endif + +void *xmon_stdin; +void *xmon_stdout; +void *xmon_stderr; + +void +xmon_init(void) +{ +} + +int +xmon_putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + xmon_putc('\r', f); + return xmon_write(f, &ch, 1) == 1? c: -1; +} + +int +xmon_putchar(int c) +{ + return xmon_putc(c, xmon_stdout); +} + +int +xmon_fputs(char *str, void *f) +{ + int n = strlen(str); + + return xmon_write(f, str, n) == n? 0: -1; +} + +int +xmon_readchar(void) +{ + char ch; + + for (;;) { + switch (xmon_read(xmon_stdin, &ch, 1)) { + case 1: + return ch; + case -1: + xmon_printf("read(stdin) returned -1\r\n", 0, 0); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int +xmon_getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = xmon_readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + xmon_putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + xmon_putchar('\a'); + else { + xmon_putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +char * +xmon_fgets(char *str, int nb, void *f) +{ + char *p; + int c; + + for (p = str; p < str + nb - 1; ) { + c = xmon_getchar(); + if (c == -1) { + if (p == str) + return 0; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + return str; +} diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/subr_prf.c linux/arch/ppc/xmon/subr_prf.c --- v2.2.11/linux/arch/ppc/xmon/subr_prf.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/subr_prf.c Wed Aug 25 17:29:46 1999 @@ -0,0 +1,47 @@ +/* + * Written by Cort Dougan to replace the version written by + * Paul Mackerras that had copyright conflicts with Linux. + * + * This file makes liberal use of the standard linux utility + * routines to reduce the size of the binary. We assume we can + * trust some parts of Linux inside the debugger. + * -- Cort (cort@cs.nmt.edu) + * + * Copyright (C) 1999 Cort Dougan. + */ + +#include +#include +#include +#include "nonstdio.h" + +extern int xmon_write(void *, void *, int); + +void +xmon_vfprintf(void *f, const char *fmt, va_list ap) +{ + char buf[2048]; + vsprintf( buf, fmt, ap ); + xmon_write( f, buf, strlen(buf) ); +} + +void +xmon_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + va_end(ap); +} + +void +xmon_fprintf(void *f, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(f, fmt, ap); + va_end(ap); +} + diff -u --recursive --new-file v2.2.11/linux/arch/ppc/xmon/xmon.c linux/arch/ppc/xmon/xmon.c --- v2.2.11/linux/arch/ppc/xmon/xmon.c Wed Dec 31 16:00:00 1969 +++ linux/arch/ppc/xmon/xmon.c Wed Aug 25 17:29:46 1999 @@ -0,0 +1,1323 @@ +/* + * Routines providing a simple monitor for use on the PowerMac. + * + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include "nonstdio.h" +#include "privinst.h" + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +static unsigned adrs; +static int size = 1; +static unsigned ndump = 64; +static unsigned nidump = 16; +static int termch; + +static u_int bus_error_jmp[100]; +#define setjmp xmon_setjmp +#define longjmp xmon_longjmp + +/* Breakpoint stuff */ +struct bpt { + unsigned address; + unsigned instr; + unsigned count; + unsigned char enabled; +}; + +#define NBPTS 16 +static struct bpt bpts[NBPTS]; +static struct bpt dabr; +static struct bpt iabr; +static unsigned bpinstr = 0x7fe00008; /* trap */ + +/* Prototypes */ +extern void (*debugger_fault_handler)(struct pt_regs *); +static int cmds(struct pt_regs *); +static int mread(unsigned, void *, int); +static int mwrite(unsigned, void *, int); +static void handle_fault(struct pt_regs *); +static void byterev(unsigned char *, int); +static void memex(void); +static int bsesc(void); +static void dump(void); +static void prdump(unsigned, int); +#ifdef __MWERKS__ +static void prndump(unsigned, int); +static int nvreadb(unsigned); +#endif +static int ppc_inst_dump(unsigned, int); +void print_address(unsigned); +static int getsp(void); +static void dump_hash_table(void); +static void backtrace(struct pt_regs *); +static void excprint(struct pt_regs *); +static void prregs(struct pt_regs *); +static void memops(int); +static void memlocate(void); +static void memzcan(void); +static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned); +int skipbl(void); +int scanhex(unsigned *valp); +static void scannl(void); +static int hexdigit(int); +void getstring(char *, int); +static void flush_input(void); +static int inchar(void); +static void take_input(char *); +/* static void openforth(void); */ +static unsigned read_spr(int); +static void write_spr(int, unsigned); +static void super_regs(void); +static void remove_bpts(void); +static void insert_bpts(void); +static struct bpt *at_breakpoint(unsigned pc); +static void bpt_cmds(void); + +extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned); +extern void printf(const char *fmt, ...); +extern int putchar(int ch); +extern int setjmp(u_int *); +extern void longjmp(u_int *, int); + +#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3]) + +static char *help_string = "\ +Commands:\n\ + d dump bytes\n\ + di dump instructions\n\ + df dump float values\n\ + dd dump double values\n\ + e print exception information\n\ + h dump hash table\n\ + m examine/change memory\n\ + mm move a block of memory\n\ + ms set a block of memory\n\ + md compare two blocks of memory\n\ + r print registers\n\ + S print special registers\n\ + t print backtrace\n\ + x exit monitor\n\ +"; + +static int xmon_trace; +#define SSTEP 1 /* stepping because of 's' command */ +#define BRSTEP 2 /* stepping over breakpoint */ + +void +xmon(struct pt_regs *excp) +{ + struct pt_regs regs; + int msr, cmd; + + printk("Entering xmon kernel debugger.\n"); + + if (excp == NULL) { + asm volatile ("stw 0,0(%0)\n\ + lwz 0,0(1)\n\ + stw 0,4(%0)\n\ + stmw 2,8(%0)" : : "b" (®s)); + regs.nip = regs.link = ((unsigned long *)regs.gpr[1])[1]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + excp = ®s; + } + + msr = get_msr(); + set_msr(msr & ~0x8000); /* disable interrupts */ + remove_bpts(); + excprint(excp); + cmd = cmds(excp); + if (cmd == 's') { + xmon_trace = SSTEP; + excp->msr |= 0x400; + } else if (at_breakpoint(excp->nip)) { + xmon_trace = BRSTEP; + excp->msr |= 0x400; + } else { + xmon_trace = 0; + insert_bpts(); + } + set_msr(msr); /* restore interrupt enable */ +} + +void +xmon_irq(int irq, void *d, struct pt_regs *regs) +{ + printf("Keyboard interrupt\n"); + xmon(regs); +} + +int +xmon_bpt(struct pt_regs *regs) +{ + struct bpt *bp; + + bp = at_breakpoint(regs->nip); + if (!bp) + return 0; + if (bp->count) { + --bp->count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + xmon(regs); + } + return 1; +} + +int +xmon_sstep(struct pt_regs *regs) +{ + if (!xmon_trace) + return 0; + if (xmon_trace == BRSTEP) { + xmon_trace = 0; + insert_bpts(); + } else { + xmon(regs); + } + return 1; +} + +int +xmon_dabr_match(struct pt_regs *regs) +{ + if (dabr.enabled && dabr.count) { + --dabr.count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + dabr.instr = regs->nip; + xmon(regs); + } + return 1; +} + +int +xmon_iabr_match(struct pt_regs *regs) +{ + if (iabr.enabled && iabr.count) { + --iabr.count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + xmon(regs); + } + return 1; +} + +static struct bpt * +at_breakpoint(unsigned pc) +{ + int i; + struct bpt *bp; + + if (dabr.enabled && pc == dabr.instr) + return &dabr; + if (iabr.enabled && pc == iabr.address) + return &iabr; + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) + if (bp->enabled && pc == bp->address) + return bp; + return 0; +} + +static void +insert_bpts() +{ + int i; + struct bpt *bp; + + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &bp->instr, 4) != 4 + || mwrite(bp->address, &bpinstr, 4) != 4) { + printf("Couldn't insert breakpoint at %x, disabling\n", + bp->address); + bp->enabled = 0; + } + } + if (dabr.enabled) + set_dabr(dabr.address); + if (iabr.enabled) + set_iabr(iabr.address); +} + +static void +remove_bpts() +{ + int i; + struct bpt *bp; + unsigned instr; + + set_dabr(0); + set_iabr(0); + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &instr, 4) == 4 + && instr == bpinstr + && mwrite(bp->address, &bp->instr, 4) != 4) + printf("Couldn't remove breakpoint at %x\n", + bp->address); + } +} + +static char *last_cmd; + +/* Command interpreting routine */ +static int +cmds(struct pt_regs *excp) +{ + int cmd; + + last_cmd = NULL; + for(;;) { + printf("mon> "); + fflush(stdout); + flush_input(); + termch = 0; + cmd = skipbl(); + if( cmd == '\n' ) { + if (last_cmd == NULL) + continue; + take_input(last_cmd); + last_cmd = NULL; + cmd = inchar(); + } + switch (cmd) { + case 'm': + cmd = inchar(); + switch (cmd) { + case 'm': + case 's': + case 'd': + memops(cmd); + break; + case 'l': + memlocate(); + break; + case 'z': + memzcan(); + break; + default: + termch = cmd; + memex(); + } + break; + case 'd': + dump(); + break; + case 'r': + if (excp != NULL) + prregs(excp); /* print regs */ + break; + case 'e': + if (excp == NULL) + printf("No exception information\n"); + else + excprint(excp); + break; + case 'S': + super_regs(); + break; + case 't': + backtrace(excp); + break; +#if 0 + case 'f': + openforth(); + break; +#endif + case 'h': + dump_hash_table(); + break; + case 's': + case 'x': + case EOF: + return cmd; + case '?': + printf(help_string); + break; + default: + printf("Unrecognized command: "); + if( ' ' < cmd && cmd <= '~' ) + putchar(cmd); + else + printf("\\x%x", cmd); + printf(" (type ? for help)\n"); + break; + case 'b': + bpt_cmds(); + break; + } + } +} + +static void +bpt_cmds(void) +{ + int cmd; + unsigned a; + int mode, i; + struct bpt *bp; + + cmd = inchar(); + switch (cmd) { + case 'd': + mode = 7; + cmd = inchar(); + if (cmd == 'r') + mode = 5; + else if (cmd == 'w') + mode = 6; + else + termch = cmd; + dabr.address = 0; + dabr.count = 0; + dabr.enabled = scanhex(&dabr.address); + scanhex(&dabr.count); + if (dabr.enabled) + dabr.address = (dabr.address & ~7) | mode; + break; + case 'i': + iabr.address = 0; + iabr.count = 0; + iabr.enabled = scanhex(&iabr.address); + if (iabr.enabled) + iabr.address |= 3; + scanhex(&iabr.count); + break; + case 'c': + if (!scanhex(&a)) { + /* clear all breakpoints */ + for (i = 0; i < NBPTS; ++i) + bpts[i].enabled = 0; + iabr.enabled = 0; + dabr.enabled = 0; + printf("All breakpoints cleared\n"); + } else { + bp = at_breakpoint(a); + if (bp == 0) { + printf("No breakpoint at %x\n", a); + } else { + bp->enabled = 0; + } + } + break; + default: + termch = cmd; + if (!scanhex(&a)) { + /* print all breakpoints */ + printf("type address count\n"); + if (dabr.enabled) { + printf("data %.8x %8x [", dabr.address & ~7, + dabr.count); + if (dabr.address & 1) + printf("r"); + if (dabr.address & 2) + printf("w"); + printf("]\n"); + } + if (iabr.enabled) + printf("inst %.8x %8x\n", iabr.address & ~3, + iabr.count); + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) + if (bp->enabled) + printf("trap %.8x %8x\n", bp->address, + bp->count); + break; + } + bp = at_breakpoint(a); + if (bp == 0) { + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) + if (!bp->enabled) + break; + if (bp >= &bpts[NBPTS]) { + printf("Sorry, no free breakpoints\n"); + break; + } + } + bp->enabled = 1; + bp->address = a; + bp->count = 0; + scanhex(&bp->count); + break; + } +} + +static void +backtrace(struct pt_regs *excp) +{ + unsigned sp; + unsigned stack[2]; + struct pt_regs regs; + extern char int_return, syscall_ret_1, syscall_ret_2; + extern char lost_irq_ret, do_bottom_half_ret, do_signal_ret; + + if (excp != NULL) + sp = excp->gpr[1]; + else + sp = getsp(); + scanhex(&sp); + scannl(); + for (; sp != 0; sp = stack[0]) { + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; + printf("%x ", stack[1]); + if (stack[1] == (unsigned) &int_return + || stack[1] == (unsigned) &syscall_ret_1 + || stack[1] == (unsigned) &syscall_ret_2 + || stack[1] == (unsigned) &lost_irq_ret + || stack[1] == (unsigned) &do_bottom_half_ret + || stack[1] == (unsigned) &do_signal_ret) { + if (mread(sp+16, ®s, sizeof(regs)) != sizeof(regs)) + break; + printf("\nexception:%x [%x] %x ", regs.trap, sp+16, + regs.nip); + sp = regs.gpr[1]; + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; + } + } + printf("\n"); +} + +int +getsp() +{ + int x; + + asm("mr %0,1" : "=r" (x) :); + return x; +} + +void +excprint(struct pt_regs *fp) +{ + printf("vector: %x at pc = %x, msr = %x, sp = %x [%x]\n", + fp->trap, fp->nip, fp->msr, fp->gpr[1], fp); + if (fp->trap == 0x300 || fp->trap == 0x600) + printf("dar = %x, dsisr = %x\n", fp->dar, fp->dsisr); + if (current) + printf("current = %x, pid = %d, comm = %s\n", + current, current->pid, current->comm); +} + +void +prregs(struct pt_regs *fp) +{ + int n; + unsigned base; + + if (scanhex(&base)) + fp = (struct pt_regs *) base; + for (n = 0; n < 32; ++n) + printf("R%.2d = %.8x%s", n, fp->gpr[n], + (n & 3) == 3? "\n": " "); + printf("pc = %.8x msr = %.8x lr = %.8x cr = %.8x\n", + fp->nip, fp->msr, fp->link, fp->ccr); + printf("ctr = %.8x xer = %.8x trap = %4x\n", + fp->ctr, fp->xer, fp->trap); +} + +unsigned int +read_spr(int n) +{ + unsigned int instrs[2]; + int (*code)(void); + + instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + store_inst(instrs); + store_inst(instrs+1); + code = (int (*)(void)) instrs; + return code(); +} + +void +write_spr(int n, unsigned int val) +{ + unsigned int instrs[2]; + int (*code)(unsigned int); + + instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + store_inst(instrs); + store_inst(instrs+1); + code = (int (*)(unsigned int)) instrs; + code(val); +} + +static unsigned int regno; +extern char exc_prolog; +extern char dec_exc; + +void +super_regs() +{ + int i, cmd; + unsigned val; + + cmd = skipbl(); + if (cmd == '\n') { + printf("msr = %x, pvr = %x\n", get_msr(), get_pvr()); + printf("sprg0-3 = %x %x %x %x\n", get_sprg0(), get_sprg1(), + get_sprg2(), get_sprg3()); + printf("srr0 = %x, srr1 = %x\n", get_srr0(), get_srr1()); + printf("sr0-15 ="); + for (i = 0; i < 16; ++i) + printf(" %x", get_sr(i)); + printf("\n"); + asm("mr %0,1" : "=r" (i) :); + printf("sp = %x ", i); + asm("mr %0,2" : "=r" (i) :); + printf("toc = %x\n", i); + return; + } + + scanhex(®no); + switch (cmd) { + case 'w': + val = read_spr(regno); + scanhex(&val); + write_spr(regno, val); + /* fall through */ + case 'r': + printf("spr %x = %x\n", regno, read_spr(regno)); + break; + case 's': + val = get_sr(regno); + scanhex(&val); + set_sr(regno, val); + break; + case 'm': + val = get_msr(); + scanhex(&val); + set_msr(val); + break; + } + scannl(); +} + +#if 0 +static void +openforth() +{ + int c; + char *p; + char cmd[1024]; + int args[5]; + extern int (*prom_entry)(int *); + + p = cmd; + c = skipbl(); + while (c != '\n') { + *p++ = c; + c = inchar(); + } + *p = 0; + args[0] = (int) "interpret"; + args[1] = 1; + args[2] = 1; + args[3] = (int) cmd; + (*prom_entry)(args); + printf("\n"); + if (args[4] != 0) + printf("error %x\n", args[4]); +} +#endif + +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va; + int found, last_found, i; + unsigned *hg, w1, last_w2, last_va0; + + last_found = 0; + hmask = hsize / 64 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 16); + w1 = 0x80000000 | (seg << 7) | (v >> 10); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 0x40; + hg = htab + ((~(v ^ seg) & hmask) * 16); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[1] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[1]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[1] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} +static unsigned hash_ctx; +static unsigned hash_start; +static unsigned hash_end; + +static void +dump_hash_table() +{ + int seg; + unsigned seg_start, seg_end; + + hash_ctx = 0; + hash_start = 0; + hash_end = 0xfffff000; + scanhex(&hash_ctx); + scanhex(&hash_start); + scanhex(&hash_end); + printf("Mappings for context %x\n", hash_ctx); + seg_start = hash_start; + for (seg = hash_start >> 28; seg <= hash_end >> 28; ++seg) { + seg_end = (seg << 28) | 0x0ffff000; + if (seg_end > hash_end) + seg_end = hash_end; + dump_hash_table_seg((hash_ctx << 4) + seg, seg_start, seg_end); + seg_start = seg_end + 0x1000; + } +} + +/* + * Stuff for reading and writing memory safely + */ +extern inline void sync(void) +{ + asm volatile("sync; isync"); +} + +extern inline void __delay(unsigned int loops) +{ + if (loops != 0) + __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : + "r" (loops) : "ctr"); +} + +int +mread(unsigned adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)q = *(short *)p; break; + case 4: *(int *)q = *(int *)p; break; + default: + for( ; n < size; ++n ) { + *q++ = *p++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } + debugger_fault_handler = 0; + return n; +} + +int +mwrite(unsigned adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)p = *(short *)q; break; + case 4: *(int *)p = *(int *)q; break; + default: + for( ; n < size; ++n ) { + *p++ = *q++; + sync(); + } + } + sync(); + n = size; + } else { + printf("*** Error writing address %x\n", adrs + n); + } + debugger_fault_handler = 0; + return n; +} + +static int fault_type; +static char *fault_chars[] = { "--", "**", "##" }; + +static void +handle_fault(struct pt_regs *regs) +{ + fault_type = regs->trap == 0x200? 0: regs->trap == 0x300? 1: 2; + longjmp(bus_error_jmp, 1); +} + +#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t)) + +void +byterev(unsigned char *val, int size) +{ + int t; + + switch (size) { + case 2: + SWAP(val[0], val[1], t); + break; + case 4: + SWAP(val[0], val[3], t); + SWAP(val[1], val[2], t); + break; + } +} + +static int brev; +static int mnoread; + +void +memex() +{ + int cmd, inc, i, nslash; + unsigned n; + unsigned char val[4]; + + last_cmd = "m\n"; + scanhex(&adrs); + while ((cmd = skipbl()) != '\n') { + switch( cmd ){ + case 'b': size = 1; break; + case 'w': size = 2; break; + case 'l': size = 4; break; + case 'r': brev = !brev; break; + case 'n': mnoread = 1; break; + case '.': mnoread = 0; break; + } + } + if( size <= 0 ) + size = 1; + else if( size > 4 ) + size = 4; + for(;;){ + if (!mnoread) + n = mread(adrs, val, size); + printf("%.8x%c", adrs, brev? 'r': ' '); + if (!mnoread) { + if (brev) + byterev(val, size); + putchar(' '); + for (i = 0; i < n; ++i) + printf("%.2x", val[i]); + for (; i < size; ++i) + printf("%s", fault_chars[fault_type]); + } + putchar(' '); + inc = size; + nslash = 0; + for(;;){ + if( scanhex(&n) ){ + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + inc = size; + } + cmd = skipbl(); + if (cmd == '\n') + break; + inc = 0; + switch (cmd) { + case '\'': + for(;;){ + n = inchar(); + if( n == '\\' ) + n = bsesc(); + else if( n == '\'' ) + break; + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + adrs += size; + } + adrs -= size; + inc = size; + break; + case ',': + adrs += size; + break; + case '.': + mnoread = 0; + break; + case ';': + break; + case 'x': + case EOF: + scannl(); + return; + case 'b': + case 'v': + size = 1; + break; + case 'w': + size = 2; + break; + case 'l': + size = 4; + break; + case '^': + adrs -= size; + break; + break; + case '/': + if (nslash > 0) + adrs -= 1 << nslash; + else + nslash = 0; + nslash += 4; + adrs += 1 << nslash; + break; + case '\\': + if (nslash < 0) + adrs += 1 << -nslash; + else + nslash = 0; + nslash -= 4; + adrs -= 1 << -nslash; + break; + case 'm': + scanhex(&adrs); + break; + case 'n': + mnoread = 1; + break; + case 'r': + brev = !brev; + break; + case '<': + n = size; + scanhex(&n); + adrs -= n; + break; + case '>': + n = size; + scanhex(&n); + adrs += n; + break; + } + } + adrs += inc; + } +} + +int +bsesc() +{ + int c; + + c = inchar(); + switch( c ){ + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 'b': c = '\b'; break; + case 't': c = '\t'; break; + } + return c; +} + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) +void +dump() +{ + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex(&adrs); + if( termch != '\n') + termch = 0; + if( c == 'i' ){ + scanhex(&nidump); + if( nidump == 0 ) + nidump = 16; + adrs += ppc_inst_dump(adrs, nidump); + last_cmd = "di\n"; + } else { + scanhex(&ndump); + if( ndump == 0 ) + ndump = 64; + prdump(adrs, ndump); + adrs += ndump; + last_cmd = "d\n"; + } +} + +void +prdump(unsigned adrs, int ndump) +{ + register int n, m, c, r, nr; + unsigned char temp[16]; + + for( n = ndump; n > 0; ){ + printf("%.8x", adrs); + putchar(' '); + r = n < 16? n: 16; + nr = mread(adrs, temp, r); + adrs += nr; + for( m = 0; m < r; ++m ){ + putchar((m & 3) == 0 && m > 0? '.': ' '); + if( m < nr ) + printf("%.2x", temp[m]); + else + printf("%s", fault_chars[fault_type]); + } + for(; m < 16; ++m ) + printf(" "); + printf(" |"); + for( m = 0; m < r; ++m ){ + if( m < nr ){ + c = temp[m]; + putchar(' ' <= c && c <= '~'? c: '.'); + } else + putchar(' '); + } + n -= r; + for(; m < 16; ++m ) + putchar(' '); + printf("|\n"); + if( nr < r ) + break; + } +} + +int +ppc_inst_dump(unsigned adr, int count) +{ + int nr, dotted; + unsigned first_adr; + unsigned long inst, last_inst; + unsigned char val[4]; + + dotted = 0; + for (first_adr = adr; count > 0; --count, adr += 4){ + nr = mread(adr, val, 4); + if( nr == 0 ){ + const char *x = fault_chars[fault_type]; + printf("%.8x %s%s%s%s\n", adr, x, x, x, x); + break; + } + inst = GETWORD(val); + if (adr > first_adr && inst == last_inst) { + if (!dotted) { + printf(" ...\n"); + dotted = 1; + } + continue; + } + dotted = 0; + last_inst = inst; + printf("%.8x ", adr); + printf("%.8x\t", inst); + print_insn_big_powerpc(stdout, inst, adr); /* always returns 4 */ + printf("\n"); + } + return adr - first_adr; +} + +void +print_address(addr) +unsigned addr; +{ + printf("0x%x", addr); +} + +/* + * Memory operations - move, set, print differences + */ +static unsigned mdest; /* destination address */ +static unsigned msrc; /* source address */ +static unsigned mval; /* byte value to set memory to */ +static unsigned mcount; /* # bytes to affect */ +static unsigned mdiffs; /* max # differences to print */ + +void +memops(int cmd) +{ + scanhex(&mdest); + if( termch != '\n' ) + termch = 0; + scanhex(cmd == 's'? &mval: &msrc); + if( termch != '\n' ) + termch = 0; + scanhex(&mcount); + switch( cmd ){ + case 'm': + memmove((void *)mdest, (void *)msrc, mcount); + break; + case 's': + memset((void *)mdest, mval, mcount); + break; + case 'd': + if( termch != '\n' ) + termch = 0; + scanhex(&mdiffs); + memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs); + break; + } +} + +void +memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr) +{ + unsigned n, prt; + + prt = 0; + for( n = nb; n > 0; --n ) + if( *p1++ != *p2++ ) + if( ++prt <= maxpr ) + printf("%.8x %.2x # %.8x %.2x\n", (unsigned)p1 - 1, + p1[-1], (unsigned)p2 - 1, p2[-1]); + if( prt > maxpr ) + printf("Total of %d differences\n", prt); +} + +static unsigned mend; +static unsigned mask; + +void +memlocate() +{ + unsigned a, n; + unsigned char val[4]; + + last_cmd = "ml"; + scanhex(&mdest); + if (termch != '\n') { + termch = 0; + scanhex(&mend); + if (termch != '\n') { + termch = 0; + scanhex(&mval); + mask = ~0; + if (termch != '\n') termch = 0; + scanhex(&mask); + } + } + n = 0; + for (a = mdest; a < mend; a += 4) { + if (mread(a, val, 4) == 4 + && ((GETWORD(val) ^ mval) & mask) == 0) { + printf("%.8x: %.8x\n", a, GETWORD(val)); + if (++n >= 10) + break; + } + } +} + +static unsigned mskip = 0x1000; +static unsigned mlim = 0xffffffff; + +void +memzcan() +{ + unsigned char v; + unsigned a; + int ok, ook; + + scanhex(&mdest); + if (termch != '\n') termch = 0; + scanhex(&mskip); + if (termch != '\n') termch = 0; + scanhex(&mlim); + ook = 0; + for (a = mdest; a < mlim; a += mskip) { + ok = mread(a, &v, 1); + if (ok && !ook) { + printf("%.8x .. ", a); + fflush(stdout); + } else if (!ok && ook) + printf("%.8x\n", a - mskip); + ook = ok; + if (a + mskip < a) + break; + } + if (ook) + printf("%.8x\n", a - mskip); +} + +/* Input scanning routines */ +int +skipbl() +{ + int c; + + if( termch != 0 ){ + c = termch; + termch = 0; + } else + c = inchar(); + while( c == ' ' || c == '\t' ) + c = inchar(); + return c; +} + +int +scanhex(vp) +unsigned *vp; +{ + int c, d; + unsigned v; + + c = skipbl(); + d = hexdigit(c); + if( d == EOF ){ + termch = c; + return 0; + } + v = 0; + do { + v = (v << 4) + d; + c = inchar(); + d = hexdigit(c); + } while( d != EOF ); + termch = c; + *vp = v; + return 1; +} + +void +scannl() +{ + int c; + + c = termch; + termch = 0; + while( c != '\n' ) + c = inchar(); +} + +int +hexdigit(c) +{ + if( '0' <= c && c <= '9' ) + return c - '0'; + if( 'A' <= c && c <= 'F' ) + return c - ('A' - 10); + if( 'a' <= c && c <= 'f' ) + return c - ('a' - 10); + return EOF; +} + +void +getstring(char *s, int size) +{ + int c; + + c = skipbl(); + do { + if( size > 1 ){ + *s++ = c; + --size; + } + c = inchar(); + } while( c != ' ' && c != '\t' && c != '\n' ); + termch = c; + *s = 0; +} + +static char line[256]; +static char *lineptr; + +void +flush_input() +{ + lineptr = NULL; +} + +int +inchar() +{ + if (lineptr == NULL || *lineptr == 0) { + if (fgets(line, sizeof(line), stdin) == NULL) { + lineptr = NULL; + return EOF; + } + lineptr = line; + } + return *lineptr++; +} + +void +take_input(str) +char *str; +{ + lineptr = str; +} diff -u --recursive --new-file v2.2.11/linux/arch/sparc/defconfig linux/arch/sparc/defconfig --- v2.2.11/linux/arch/sparc/defconfig Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc/defconfig Wed Aug 25 17:29:46 1999 @@ -305,6 +305,7 @@ # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_14 is not set # CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set diff -u --recursive --new-file v2.2.11/linux/arch/sparc/kernel/init_task.c linux/arch/sparc/kernel/init_task.c --- v2.2.11/linux/arch/sparc/kernel/init_task.c Tue Oct 27 09:52:20 1998 +++ linux/arch/sparc/kernel/init_task.c Wed Aug 25 17:29:46 1999 @@ -6,7 +6,6 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff -u --recursive --new-file v2.2.11/linux/arch/sparc/mm/iommu.c linux/arch/sparc/mm/iommu.c --- v2.2.11/linux/arch/sparc/mm/iommu.c Tue May 11 08:24:31 1999 +++ linux/arch/sparc/mm/iommu.c Wed Aug 25 17:29:46 1999 @@ -1,4 +1,4 @@ -/* $Id: iommu.c,v 1.10 1999/05/07 17:03:34 jj Exp $ +/* $Id: iommu.c,v 1.10.2.1 1999/08/13 12:35:35 davem Exp $ * iommu.c: IOMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -224,6 +224,11 @@ pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; + + if (viking_mxcc_present) + viking_mxcc_flush_page(page); + else if (viking_flush) + viking_flush_page(page); pgdp = pgd_offset(init_task.mm, addr); pmdp = pmd_offset(pgdp, addr); diff -u --recursive --new-file v2.2.11/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig --- v2.2.11/linux/arch/sparc64/defconfig Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/defconfig Wed Aug 25 17:29:46 1999 @@ -349,6 +349,7 @@ # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_14 is not set # CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set diff -u --recursive --new-file v2.2.11/linux/arch/sparc64/kernel/init_task.c linux/arch/sparc64/kernel/init_task.c --- v2.2.11/linux/arch/sparc64/kernel/init_task.c Tue Apr 14 17:44:20 1998 +++ linux/arch/sparc64/kernel/init_task.c Wed Aug 25 17:29:46 1999 @@ -6,7 +6,6 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; diff -u --recursive --new-file v2.2.11/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c --- v2.2.11/linux/arch/sparc64/kernel/process.c Tue May 11 08:24:32 1999 +++ linux/arch/sparc64/kernel/process.c Wed Aug 25 17:29:46 1999 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.92 1999/05/08 23:04:48 davem Exp $ +/* $Id: process.c,v 1.92.2.1 1999/08/12 11:30:31 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -652,10 +652,14 @@ * a system call from a "real" process, but the process memory space will * not be free'd until both the parent and the child have exited. */ -pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +pid_t kernel_thread(int (*__fn)(void *), void * __arg, unsigned long flags) { + register int (*fn)(void *) asm("g2"); + register void *arg asm("g3"); long retval; + fn = __fn; + arg = __arg; __asm__ __volatile("mov %1, %%g1\n\t" "mov %2, %%o0\n\t" /* Clone flags. */ "mov 0, %%o1\n\t" /* usp arg == 0 */ diff -u --recursive --new-file v2.2.11/linux/arch/sparc64/math-emu/op-2.h linux/arch/sparc64/math-emu/op-2.h --- v2.2.11/linux/arch/sparc64/math-emu/op-2.h Wed Mar 10 16:53:37 1999 +++ linux/arch/sparc64/math-emu/op-2.h Wed Aug 25 17:29:46 1999 @@ -90,9 +90,9 @@ #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) + (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) + (X##_f1 > Y##_f1 || (X##_f1 == Y##_f1 && X##_f0 >= Y##_f0)) #define _FP_ZEROFRAC_2 0, 0 #define _FP_MINFRAC_2 0, 1 diff -u --recursive --new-file v2.2.11/linux/arch/sparc64/math-emu/op-common.h linux/arch/sparc64/math-emu/op-common.h --- v2.2.11/linux/arch/sparc64/math-emu/op-common.h Mon Aug 9 16:05:55 1999 +++ linux/arch/sparc64/math-emu/op-common.h Wed Aug 25 17:29:46 1999 @@ -23,15 +23,15 @@ if (_FP_FRAC_ZEROP_##wc(X)) \ X##_c = FP_CLS_ZERO; \ else \ - /* a denormalized number */ \ - __FP_UNPACK_DENORM(fs, wc, X) \ + /* A denormalized number. */ \ + __FP_UNPACK_DENORM(fs, wc, X); \ 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 */ \ + /* We don't differentiate between signaling and quiet nans. */ \ X##_c = FP_CLS_NAN; \ break; \ } \ diff -u --recursive --new-file v2.2.11/linux/drivers/block/DAC960.c linux/drivers/block/DAC960.c --- v2.2.11/linux/drivers/block/DAC960.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/DAC960.c Wed Aug 25 17:29:46 1999 @@ -19,8 +19,8 @@ */ -#define DAC960_DriverVersion "2.2.2" -#define DAC960_DriverDate "3 July 1999" +#define DAC960_DriverVersion "2.2.4" +#define DAC960_DriverDate "23 August 1999" #include @@ -478,6 +478,7 @@ unsigned long BaseAddress0 = PCI_Device->base_address[0]; unsigned long BaseAddress1 = PCI_Device->base_address[1]; unsigned short SubsystemVendorID, SubsystemDeviceID; + int CommandIdentifier; pci_read_config_word(PCI_Device, PCI_SUBSYSTEM_VENDOR_ID, &SubsystemVendorID); pci_read_config_word(PCI_Device, PCI_SUBSYSTEM_ID, @@ -584,9 +585,15 @@ break; } DAC960_ActiveControllerCount++; - Controller->Commands[0].Controller = Controller; - Controller->Commands[0].Next = NULL; - Controller->FreeCommands = &Controller->Commands[0]; + for (CommandIdentifier = 0; + CommandIdentifier < DAC960_MaxChannels; + CommandIdentifier++) + { + Controller->Commands[CommandIdentifier].Controller = Controller; + Controller->Commands[CommandIdentifier].Next = + Controller->FreeCommands; + Controller->FreeCommands = &Controller->Commands[CommandIdentifier]; + } continue; Failure: if (IO_Address == 0) @@ -754,14 +761,13 @@ /* - DAC960_ReportControllerConfiguration reports the configuration of + DAC960_ReportControllerConfiguration reports the Configuration Information of Controller. */ static boolean DAC960_ReportControllerConfiguration(DAC960_Controller_T *Controller) { - int LogicalDriveNumber, Channel, TargetID; DAC960_Info("Configuring Mylex %s PCI RAID Controller\n", Controller, Controller->ModelName); DAC960_Info(" Firmware Version: %s, Channels: %d, Memory Size: %dMB\n", @@ -793,40 +799,199 @@ Controller->GeometryTranslationSectors); if (Controller->SAFTE_EnclosureManagementEnabled) DAC960_Info(" SAF-TE Enclosure Management Enabled\n", Controller); + return true; +} + + +/* + DAC960_ReadDeviceConfiguration reads the Device Configuration Information by + requesting the SCSI Inquiry and SCSI Inquiry Unit Serial Number information + for each device connected to Controller. +*/ + +static boolean DAC960_ReadDeviceConfiguration(DAC960_Controller_T *Controller) +{ + DAC960_DCDB_T DCDBs[DAC960_MaxChannels], *DCDB; + Semaphore_T Semaphores[DAC960_MaxChannels], *Semaphore; + unsigned long ProcessorFlags; + int Channel, TargetID; + for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++) + { + for (Channel = 0; Channel < Controller->Channels; Channel++) + { + DAC960_Command_T *Command = &Controller->Commands[Channel]; + DAC960_SCSI_Inquiry_T *InquiryStandardData = + &Controller->InquiryStandardData[Channel][TargetID]; + InquiryStandardData->PeripheralDeviceType = 0x1F; + Semaphore = &Semaphores[Channel]; + *Semaphore = MUTEX_LOCKED; + DCDB = &DCDBs[Channel]; + DAC960_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + Command->Semaphore = Semaphore; + Command->CommandMailbox.Type3.CommandOpcode = DAC960_DCDB; + Command->CommandMailbox.Type3.BusAddress = Virtual_to_Bus(DCDB); + DCDB->Channel = Channel; + DCDB->TargetID = TargetID; + DCDB->Direction = DAC960_DCDB_DataTransferDeviceToSystem; + DCDB->EarlyStatus = false; + DCDB->Timeout = DAC960_DCDB_Timeout_10_seconds; + DCDB->NoAutomaticRequestSense = false; + DCDB->DisconnectPermitted = true; + DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_T); + DCDB->BusAddress = Virtual_to_Bus(InquiryStandardData); + DCDB->CDBLength = 6; + DCDB->TransferLengthHigh4 = 0; + DCDB->SenseLength = sizeof(DCDB->SenseData); + DCDB->CDB[0] = 0x12; /* INQUIRY */ + DCDB->CDB[1] = 0; /* EVPD = 0 */ + DCDB->CDB[2] = 0; /* Page Code */ + DCDB->CDB[3] = 0; /* Reserved */ + DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_T); + DCDB->CDB[5] = 0; /* Control */ + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + DAC960_QueueCommand(Command); + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); + } + for (Channel = 0; Channel < Controller->Channels; Channel++) + { + DAC960_Command_T *Command = &Controller->Commands[Channel]; + DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber = + &Controller->InquiryUnitSerialNumber[Channel][TargetID]; + InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F; + Semaphore = &Semaphores[Channel]; + down(Semaphore); + if (Command->CommandStatus != DAC960_NormalCompletion) continue; + Command->Semaphore = Semaphore; + DCDB = &DCDBs[Channel]; + DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T); + DCDB->BusAddress = Virtual_to_Bus(InquiryUnitSerialNumber); + DCDB->SenseLength = sizeof(DCDB->SenseData); + DCDB->CDB[0] = 0x12; /* INQUIRY */ + DCDB->CDB[1] = 1; /* EVPD = 1 */ + DCDB->CDB[2] = 0x80; /* Page Code */ + DCDB->CDB[3] = 0; /* Reserved */ + DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T); + DCDB->CDB[5] = 0; /* Control */ + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + DAC960_QueueCommand(Command); + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); + down(Semaphore); + } + } + return true; +} + + +/* + DAC960_ReportDeviceConfiguration reports the Device Configuration Information + of Controller. +*/ + +static boolean DAC960_ReportDeviceConfiguration(DAC960_Controller_T *Controller) +{ + int LogicalDriveNumber, Channel, TargetID; DAC960_Info(" Physical Devices:\n", Controller); for (Channel = 0; Channel < Controller->Channels; Channel++) for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++) { + DAC960_SCSI_Inquiry_T *InquiryStandardData = + &Controller->InquiryStandardData[Channel][TargetID]; + DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber = + &Controller->InquiryUnitSerialNumber[Channel][TargetID]; DAC960_DeviceState_T *DeviceState = &Controller->DeviceState[Controller->DeviceStateIndex] [Channel][TargetID]; - if (!DeviceState->Present) continue; - switch (DeviceState->DeviceType) + DAC960_ErrorTable_T *ErrorTable = + &Controller->ErrorTable[Controller->ErrorTableIndex]; + DAC960_ErrorTableEntry_T *ErrorEntry = + &ErrorTable->ErrorTableEntries[Channel][TargetID]; + char Vendor[1+sizeof(InquiryStandardData->VendorIdentification)]; + char Model[1+sizeof(InquiryStandardData->ProductIdentification)]; + char Revision[1+sizeof(InquiryStandardData->ProductRevisionLevel)]; + char SerialNumber[1+sizeof(InquiryUnitSerialNumber + ->ProductSerialNumber)]; + int i; + if (InquiryStandardData->PeripheralDeviceType == 0x1F) continue; + for (i = 0; i < sizeof(Vendor)-1; i++) + { + unsigned char VendorCharacter = + InquiryStandardData->VendorIdentification[i]; + Vendor[i] = (VendorCharacter >= ' ' && VendorCharacter <= '~' + ? VendorCharacter : ' '); + } + Vendor[sizeof(Vendor)-1] = '\0'; + for (i = 0; i < sizeof(Model)-1; i++) + { + unsigned char ModelCharacter = + InquiryStandardData->ProductIdentification[i]; + Model[i] = (ModelCharacter >= ' ' && ModelCharacter <= '~' + ? ModelCharacter : ' '); + } + Model[sizeof(Model)-1] = '\0'; + for (i = 0; i < sizeof(Revision)-1; i++) + { + unsigned char RevisionCharacter = + InquiryStandardData->ProductRevisionLevel[i]; + Revision[i] = (RevisionCharacter >= ' ' && RevisionCharacter <= '~' + ? RevisionCharacter : ' '); + } + Revision[sizeof(Revision)-1] = '\0'; + DAC960_Info(" %d:%d%s Vendor: %s Model: %s Revision: %s\n", + Controller, Channel, TargetID, (TargetID < 10 ? " " : ""), + Vendor, Model, Revision); + if (InquiryUnitSerialNumber->PeripheralDeviceType != 0x1F) + { + int SerialNumberLength = InquiryUnitSerialNumber->PageLength; + if (SerialNumberLength > + sizeof(InquiryUnitSerialNumber->ProductSerialNumber)) + SerialNumberLength = + sizeof(InquiryUnitSerialNumber->ProductSerialNumber); + for (i = 0; i < SerialNumberLength; i++) + { + unsigned char SerialNumberCharacter = + InquiryUnitSerialNumber->ProductSerialNumber[i]; + SerialNumber[i] = + (SerialNumberCharacter >= ' ' && SerialNumberCharacter <= '~' + ? SerialNumberCharacter : ' '); + } + SerialNumber[SerialNumberLength] = '\0'; + DAC960_Info(" Serial Number: %s\n", + Controller, SerialNumber); + } + if (DeviceState->Present && DeviceState->DeviceType == DAC960_DiskType) { - case DAC960_OtherType: - DAC960_Info(" %d:%d - Other\n", Controller, Channel, TargetID); - break; - case DAC960_DiskType: - DAC960_Info(" %d:%d - Disk: %s, %d blocks\n", Controller, - Channel, TargetID, - (DeviceState->DeviceState == DAC960_Device_Dead - ? "Dead" - : DeviceState->DeviceState == DAC960_Device_WriteOnly + if (Controller->DeviceResetCount[Channel][TargetID] > 0) + DAC960_Info(" Disk Status: %s, %d blocks, %d resets\n", + Controller, + (DeviceState->DeviceState == DAC960_Device_Dead + ? "Dead" + : DeviceState->DeviceState == DAC960_Device_WriteOnly + ? "Write-Only" + : DeviceState->DeviceState == DAC960_Device_Online + ? "Online" : "Standby"), + DeviceState->DiskSize, + Controller->DeviceResetCount[Channel][TargetID]); + else + DAC960_Info(" Disk Status: %s, %d blocks\n", Controller, + (DeviceState->DeviceState == DAC960_Device_Dead + ? "Dead" + : DeviceState->DeviceState == DAC960_Device_WriteOnly ? "Write-Only" : DeviceState->DeviceState == DAC960_Device_Online - ? "Online" : "Standby"), - DeviceState->DiskSize); - break; - case DAC960_SequentialType: - DAC960_Info(" %d:%d - Sequential\n", Controller, - Channel, TargetID); - break; - case DAC960_CDROM_or_WORM_Type: - DAC960_Info(" %d:%d - CD-ROM or WORM\n", Controller, - Channel, TargetID); - break; + ? "Online" : "Standby"), + DeviceState->DiskSize); } - + if (ErrorEntry->ParityErrorCount > 0 || + ErrorEntry->SoftErrorCount > 0 || + ErrorEntry->HardErrorCount > 0 || + ErrorEntry->MiscErrorCount > 0) + DAC960_Info(" Errors - Parity: %d, Soft: %d, " + "Hard: %d, Misc: %d\n", Controller, + ErrorEntry->ParityErrorCount, + ErrorEntry->SoftErrorCount, + ErrorEntry->HardErrorCount, + ErrorEntry->MiscErrorCount); } DAC960_Info(" Logical Drives:\n", Controller); for (LogicalDriveNumber = 0; @@ -982,6 +1147,8 @@ { if (DAC960_ReadControllerConfiguration(Controller) && DAC960_ReportControllerConfiguration(Controller) && + DAC960_ReadDeviceConfiguration(Controller) && + DAC960_ReportDeviceConfiguration(Controller) && DAC960_RegisterBlockDevice(Controller)) { /* @@ -1625,7 +1792,7 @@ Controller->NeedErrorTableInformation = true; Controller->NeedDeviceStateInformation = true; Controller->DeviceStateChannel = 0; - Controller->DeviceStateTargetID = 0; + Controller->DeviceStateTargetID = -1; Controller->SecondaryMonitoringTime = jiffies; } if (NewEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress || @@ -1705,13 +1872,17 @@ EventLogEntry->TargetID, DAC960_EventMessages[ AdditionalSenseCodeQualifier]); + else if (SenseKey == 6 && AdditionalSenseCode == 0x29) + { + if (Controller->MonitoringTimerCount > 0) + Controller->DeviceResetCount[EventLogEntry->Channel] + [EventLogEntry->TargetID]++; + } else if (!(SenseKey == 0 || (SenseKey == 2 && AdditionalSenseCode == 0x04 && (AdditionalSenseCodeQualifier == 0x01 || - AdditionalSenseCodeQualifier == 0x02)) || - (SenseKey == 6 && AdditionalSenseCode == 0x29 && - Controller->MonitoringTimerCount == 0))) + AdditionalSenseCodeQualifier == 0x02)))) { DAC960_Critical("Physical Drive %d:%d Error Log: " "Sense Key = %d, ASC = %02X, ASCQ = %02X\n", @@ -1793,10 +1964,11 @@ : NewDeviceState->DeviceState == DAC960_Device_Online ? "ONLINE" : "STANDBY")); - if (++Controller->DeviceStateTargetID == DAC960_MaxTargets) + if (OldDeviceState->DeviceState == DAC960_Device_Dead && + NewDeviceState->DeviceState != DAC960_Device_Dead) { - Controller->DeviceStateChannel++; - Controller->DeviceStateTargetID = 0; + Controller->NeedDeviceInquiryInformation = true; + Controller->NeedDeviceSerialNumberInformation = true; } } else if (CommandOpcode == DAC960_GetLogicalDriveInformation) @@ -1948,6 +2120,76 @@ } if (Controller->NeedDeviceStateInformation) { + if (Controller->NeedDeviceInquiryInformation) + { + DAC960_DCDB_T *DCDB = &Controller->MonitoringDCDB; + DAC960_SCSI_Inquiry_T *InquiryStandardData = + &Controller->InquiryStandardData + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]; + InquiryStandardData->PeripheralDeviceType = 0x1F; + Command->CommandMailbox.Type3.CommandOpcode = DAC960_DCDB; + Command->CommandMailbox.Type3.BusAddress = Virtual_to_Bus(DCDB); + DCDB->Channel = Controller->DeviceStateChannel; + DCDB->TargetID = Controller->DeviceStateTargetID; + DCDB->Direction = DAC960_DCDB_DataTransferDeviceToSystem; + DCDB->EarlyStatus = false; + DCDB->Timeout = DAC960_DCDB_Timeout_10_seconds; + DCDB->NoAutomaticRequestSense = false; + DCDB->DisconnectPermitted = true; + DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_T); + DCDB->BusAddress = Virtual_to_Bus(InquiryStandardData); + DCDB->CDBLength = 6; + DCDB->TransferLengthHigh4 = 0; + DCDB->SenseLength = sizeof(DCDB->SenseData); + DCDB->CDB[0] = 0x12; /* INQUIRY */ + DCDB->CDB[1] = 0; /* EVPD = 0 */ + DCDB->CDB[2] = 0; /* Page Code */ + DCDB->CDB[3] = 0; /* Reserved */ + DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_T); + DCDB->CDB[5] = 0; /* Control */ + DAC960_QueueCommand(Command); + Controller->NeedDeviceInquiryInformation = false; + return; + } + if (Controller->NeedDeviceSerialNumberInformation) + { + DAC960_DCDB_T *DCDB = &Controller->MonitoringDCDB; + DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber = + &Controller->InquiryUnitSerialNumber + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]; + InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F; + Command->CommandMailbox.Type3.CommandOpcode = DAC960_DCDB; + Command->CommandMailbox.Type3.BusAddress = Virtual_to_Bus(DCDB); + DCDB->Channel = Controller->DeviceStateChannel; + DCDB->TargetID = Controller->DeviceStateTargetID; + DCDB->Direction = DAC960_DCDB_DataTransferDeviceToSystem; + DCDB->EarlyStatus = false; + DCDB->Timeout = DAC960_DCDB_Timeout_10_seconds; + DCDB->NoAutomaticRequestSense = false; + DCDB->DisconnectPermitted = true; + DCDB->TransferLength = + sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T); + DCDB->BusAddress = Virtual_to_Bus(InquiryUnitSerialNumber); + DCDB->CDBLength = 6; + DCDB->TransferLengthHigh4 = 0; + DCDB->SenseLength = sizeof(DCDB->SenseData); + DCDB->CDB[0] = 0x12; /* INQUIRY */ + DCDB->CDB[1] = 1; /* EVPD = 1 */ + DCDB->CDB[2] = 0x80; /* Page Code */ + DCDB->CDB[3] = 0; /* Reserved */ + DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T); + DCDB->CDB[5] = 0; /* Control */ + DAC960_QueueCommand(Command); + Controller->NeedDeviceSerialNumberInformation = false; + return; + } + if (++Controller->DeviceStateTargetID == DAC960_MaxTargets) + { + Controller->DeviceStateChannel++; + Controller->DeviceStateTargetID = 0; + } while (Controller->DeviceStateChannel < Controller->Channels) { DAC960_DeviceState_T *OldDeviceState = @@ -3078,9 +3320,8 @@ DAC960_ProcReadStatus implements reading /proc/rd/status. */ -static ssize_t DAC960_ProcReadStatus(char *Page, char **Start, - off_t Offset, int Count, - int *EOF, void *Data) +static int DAC960_ProcReadStatus(char *Page, char **Start, off_t Offset, + int Count, int *EOF, void *Data) { char *StatusMessage = "OK\n"; int ControllerNumber, BytesAvailable; @@ -3107,6 +3348,7 @@ *EOF = true; } if (Count <= 0) return 0; + *Start = Page; memcpy(Page, &StatusMessage[Offset], Count); return Count; } @@ -3116,9 +3358,8 @@ DAC960_ProcReadInitialStatus implements reading /proc/rd/cN/initial_status. */ -static ssize_t DAC960_ProcReadInitialStatus(char *Page, char **Start, - off_t Offset, int Count, - int *EOF, void *Data) +static int DAC960_ProcReadInitialStatus(char *Page, char **Start, off_t Offset, + int Count, int *EOF, void *Data) { DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data; int BytesAvailable = Controller->InitialStatusLength - Offset; @@ -3128,6 +3369,7 @@ *EOF = true; } if (Count <= 0) return 0; + *Start = Page; memcpy(Page, &Controller->InitialStatusBuffer[Offset], Count); return Count; } @@ -3137,29 +3379,35 @@ DAC960_ProcReadCurrentStatus implements reading /proc/rd/cN/current_status. */ -static ssize_t DAC960_ProcReadCurrentStatus(char *Page, char **Start, - off_t Offset, int Count, - int *EOF, void *Data) +static int DAC960_ProcReadCurrentStatus(char *Page, char **Start, off_t Offset, + int Count, int *EOF, void *Data) { DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data; int BytesAvailable; - Controller->CurrentStatusLength = 0; - DAC960_AnnounceDriver(Controller); - DAC960_ReportControllerConfiguration(Controller); - Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' '; - Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' '; - if (Controller->RebuildProgressLength > 0) - { - strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength], - Controller->RebuildProgressBuffer); - Controller->CurrentStatusLength += Controller->RebuildProgressLength; - } - else + if (jiffies != Controller->LastCurrentStatusTime) { - char *StatusMessage = "No Rebuild or Consistency Check in Progress\n"; - strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength], - StatusMessage); - Controller->CurrentStatusLength += strlen(StatusMessage); + Controller->CurrentStatusLength = 0; + DAC960_AnnounceDriver(Controller); + DAC960_ReportControllerConfiguration(Controller); + DAC960_ReportDeviceConfiguration(Controller); + Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' '; + Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' '; + if (Controller->RebuildProgressLength > 0) + { + strcpy(&Controller->CurrentStatusBuffer + [Controller->CurrentStatusLength], + Controller->RebuildProgressBuffer); + Controller->CurrentStatusLength += Controller->RebuildProgressLength; + } + else + { + char *StatusMessage = "No Rebuild or Consistency Check in Progress\n"; + strcpy(&Controller->CurrentStatusBuffer + [Controller->CurrentStatusLength], + StatusMessage); + Controller->CurrentStatusLength += strlen(StatusMessage); + } + Controller->LastCurrentStatusTime = jiffies; } BytesAvailable = Controller->CurrentStatusLength - Offset; if (Count >= BytesAvailable) @@ -3168,6 +3416,7 @@ *EOF = true; } if (Count <= 0) return 0; + *Start = Page; memcpy(Page, &Controller->CurrentStatusBuffer[Offset], Count); return Count; } @@ -3177,9 +3426,8 @@ DAC960_ProcReadUserCommand implements reading /proc/rd/cN/user_command. */ -static ssize_t DAC960_ProcReadUserCommand(char *Page, char **Start, - off_t Offset, int Count, - int *EOF, void *Data) +static int DAC960_ProcReadUserCommand(char *Page, char **Start, off_t Offset, + int Count, int *EOF, void *Data) { DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data; int BytesAvailable = Controller->UserStatusLength - Offset; @@ -3189,6 +3437,7 @@ *EOF = true; } if (Count <= 0) return 0; + *Start = Page; memcpy(Page, &Controller->UserStatusBuffer[Offset], Count); return Count; } @@ -3198,8 +3447,8 @@ DAC960_ProcWriteUserCommand implements writing /proc/rd/cN/user_command. */ -static ssize_t DAC960_ProcWriteUserCommand(File_T *File, const char *Buffer, - unsigned long Count, void *Data) +static int DAC960_ProcWriteUserCommand(File_T *File, const char *Buffer, + unsigned long Count, void *Data) { DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data; char CommandBuffer[80]; diff -u --recursive --new-file v2.2.11/linux/drivers/block/DAC960.h linux/drivers/block/DAC960.h --- v2.2.11/linux/drivers/block/DAC960.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/DAC960.h Wed Aug 25 17:29:46 1999 @@ -672,6 +672,57 @@ /* + Define the SCSI INQUIRY Standard Data reply structure. +*/ + +typedef struct DAC960_SCSI_Inquiry +{ + unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */ + unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */ + unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */ + boolean RMB:1; /* Byte 1 Bit 7 */ + unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */ + unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */ + unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */ + unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */ + unsigned char :2; /* Byte 3 Bits 4-5 */ + boolean TrmIOP:1; /* Byte 3 Bit 6 */ + boolean AENC:1; /* Byte 3 Bit 7 */ + unsigned char AdditionalLength; /* Byte 4 */ + unsigned char :8; /* Byte 5 */ + unsigned char :8; /* Byte 6 */ + boolean SftRe:1; /* Byte 7 Bit 0 */ + boolean CmdQue:1; /* Byte 7 Bit 1 */ + boolean :1; /* Byte 7 Bit 2 */ + boolean Linked:1; /* Byte 7 Bit 3 */ + boolean Sync:1; /* Byte 7 Bit 4 */ + boolean WBus16:1; /* Byte 7 Bit 5 */ + boolean WBus32:1; /* Byte 7 Bit 6 */ + boolean RelAdr:1; /* Byte 7 Bit 7 */ + unsigned char VendorIdentification[8]; /* Bytes 8-15 */ + unsigned char ProductIdentification[16]; /* Bytes 16-31 */ + unsigned char ProductRevisionLevel[4]; /* Bytes 32-35 */ +} +DAC960_SCSI_Inquiry_T; + + +/* + Define the SCSI INQUIRY Unit Serial Number reply structure. +*/ + +typedef struct DAC960_SCSI_Inquiry_UnitSerialNumber +{ + unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */ + unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */ + unsigned char PageCode; /* Byte 1 */ + unsigned char :8; /* Byte 2 */ + unsigned char PageLength; /* Byte 3 */ + unsigned char ProductSerialNumber[28]; /* Bytes 4 - 31 */ +} +DAC960_SCSI_Inquiry_UnitSerialNumber_T; + + +/* Define the Scatter/Gather List Type 1 32 Bit Address 32 Bit Byte Count structure. */ @@ -977,12 +1028,12 @@ /* - Define the Controller Line, Status Buffer, Rebuild Progress, and - User Message Sizes. + Define the Controller Line Buffer, Status Buffer, Rebuild Progress, + and User Message Sizes. */ #define DAC960_LineBufferSize 100 -#define DAC960_StatusBufferSize 5000 +#define DAC960_StatusBufferSize 16384 #define DAC960_RebuildProgressSize 200 #define DAC960_UserMessageSize 200 @@ -1183,6 +1234,7 @@ unsigned long MonitoringTimerCount; unsigned long SecondaryMonitoringTime; unsigned long LastProgressReportTime; + unsigned long LastCurrentStatusTime; boolean DualModeMemoryMailboxInterface; boolean SAFTE_EnclosureManagementEnabled; boolean ControllerInitialized; @@ -1190,6 +1242,8 @@ boolean NeedLogicalDriveInformation; boolean NeedErrorTableInformation; boolean NeedDeviceStateInformation; + boolean NeedDeviceInquiryInformation; + boolean NeedDeviceSerialNumberInformation; boolean NeedRebuildProgress; boolean NeedConsistencyCheckProgress; boolean EphemeralProgressMessage; @@ -1209,6 +1263,7 @@ PROC_DirectoryEntry_T CurrentStatusProcEntry; PROC_DirectoryEntry_T UserCommandProcEntry; WaitQueue_T *CommandWaitQueue; + DAC960_DCDB_T MonitoringDCDB; DAC960_Enquiry_T Enquiry[2]; DAC960_ErrorTable_T ErrorTable[2]; DAC960_EventLogEntry_T EventLogEntry; @@ -1219,12 +1274,17 @@ DAC960_LogicalDriveState_T LogicalDriveInitialState[DAC960_MaxLogicalDrives]; DAC960_DeviceState_T DeviceState[2][DAC960_MaxChannels][DAC960_MaxTargets]; DAC960_Command_T Commands[DAC960_MaxDriverQueueDepth]; + DAC960_SCSI_Inquiry_T + InquiryStandardData[DAC960_MaxChannels][DAC960_MaxTargets]; + DAC960_SCSI_Inquiry_UnitSerialNumber_T + InquiryUnitSerialNumber[DAC960_MaxChannels][DAC960_MaxTargets]; DiskPartition_T DiskPartitions[DAC960_MinorCount]; int LogicalDriveUsageCount[DAC960_MaxLogicalDrives]; int PartitionSizes[DAC960_MinorCount]; int BlockSizes[DAC960_MinorCount]; int MaxSectorsPerRequest[DAC960_MinorCount]; int MaxSegmentsPerRequest[DAC960_MinorCount]; + int DeviceResetCount[DAC960_MaxChannels][DAC960_MaxTargets]; boolean DirectCommandActive[DAC960_MaxChannels][DAC960_MaxTargets]; char InitialStatusBuffer[DAC960_StatusBufferSize]; char CurrentStatusBuffer[DAC960_StatusBufferSize]; diff -u --recursive --new-file v2.2.11/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v2.2.11/linux/drivers/block/floppy.c Wed Jun 9 16:42:49 1999 +++ linux/drivers/block/floppy.c Wed Aug 25 17:29:46 1999 @@ -106,6 +106,12 @@ * 1998/09/20 -- David Weinehall -- Added slow-down code for buggy PS/2-drives. */ +/* + * 1999/08/13 -- Paul Slootman -- floppy stopped working on Alpha after 24 + * days, 6 hours, 32 minutes and 32 seconds (i.e. MAXINT jiffies; ints were + * being used to store jiffies, which are unsigned longs). + */ + #define FLOPPY_SANITY_CHECK #undef FLOPPY_SILENT_DCL_CLEAR @@ -604,10 +610,10 @@ #define OLOGSIZE 20 static void (*lasthandler)(void) = NULL; -static int interruptjiffies=0; -static int resultjiffies=0; +static unsigned long interruptjiffies=0; +static unsigned long resultjiffies=0; static int resultsize=0; -static int lastredo=0; +static unsigned long lastredo=0; static struct output_log { unsigned char data; @@ -627,7 +633,7 @@ drive = current_drive; del_timer(&fd_timeout); if (drive < 0 || drive > N_DRIVE) { - fd_timeout.expires = jiffies + 20*HZ; + fd_timeout.expires = jiffies + 20UL*HZ; drive=0; } else fd_timeout.expires = jiffies + UDP->timeout; @@ -710,7 +716,7 @@ #ifdef DCL_DEBUG if (UDP->flags & FD_DEBUG){ DPRINT("checking disk change line for drive %d\n",drive); - DPRINT("jiffies=%ld\n", jiffies); + DPRINT("jiffies=%lu\n", jiffies); DPRINT("disk change line=%x\n",fd_inb(FD_DIR)&0x80); DPRINT("flags=%lx\n",UDRS->flags); } @@ -1004,7 +1010,7 @@ } /* waits for a delay (spinup or select) to pass */ -static int wait_for_completion(int delay, timeout_fn function) +static int wait_for_completion(unsigned long delay, timeout_fn function) { if (FDCS->reset){ reset_fdc(); /* do the reset during sleep to win time @@ -1275,7 +1281,7 @@ static void fdc_specify(void) { unsigned char spec1, spec2; - int srt, hlt, hut; + unsigned long srt, hlt, hut; unsigned long dtr = NOMINAL_DTR; unsigned long scale_dtr = NOMINAL_DTR; int hlt_max_code = 0x7f; @@ -1365,7 +1371,7 @@ * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies) */ FDCS->dtr = raw_cmd->rate & 3; - return(wait_for_completion(jiffies+2*HZ/100, + return(wait_for_completion(jiffies+2UL*HZ/100, (timeout_fn) floppy_ready)); } /* fdc_dtr */ @@ -1461,7 +1467,8 @@ */ static void setup_rw_floppy(void) { - int i,ready_date,r, flags,dflags; + int i,r, flags,dflags; + unsigned long ready_date; timeout_fn function; flags = raw_cmd->flags; @@ -1534,7 +1541,7 @@ #ifdef DCL_DEBUG if (DP->flags & FD_DEBUG){ DPRINT("clearing NEWCHANGE flag because of effective seek\n"); - DPRINT("jiffies=%ld\n", jiffies); + DPRINT("jiffies=%lu\n", jiffies); } #endif CLEARF(FD_DISK_NEWCHANGE); /* effective seek */ @@ -1824,20 +1831,20 @@ printk("\n"); printk("floppy driver state\n"); printk("-------------------\n"); - printk("now=%ld last interrupt=%d last called handler=%p\n", - jiffies, interruptjiffies, lasthandler); + printk("now=%lu last interrupt=%lu diff=%lu last called handler=%p\n", + jiffies, interruptjiffies, jiffies-interruptjiffies, lasthandler); #ifdef FLOPPY_SANITY_CHECK printk("timeout_message=%s\n", timeout_message); printk("last output bytes:\n"); for (i=0; i < OLOGSIZE; i++) - printk("%2x %2x %ld\n", + printk("%2x %2x %lu\n", output_log[(i+output_log_pos) % OLOGSIZE].data, output_log[(i+output_log_pos) % OLOGSIZE].status, output_log[(i+output_log_pos) % OLOGSIZE].jiffies); - printk("last result at %d\n", resultjiffies); - printk("last redo_fd_request at %d\n", lastredo); + printk("last result at %lu\n", resultjiffies); + printk("last redo_fd_request at %lu\n", lastredo); for (i=0; icheckfreq < jiffies - UDRS->last_checked){ + if (UDP->checkfreq < (int)(jiffies - UDRS->last_checked)) { lock_fdc(drive,0); poll_drive(0,0); process_fd_request(); @@ -3989,7 +3996,7 @@ else default_drive_params[i].params.flags &= ~param2; } - DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param); + DPRINT("%s flag 0x%x\n", param ? "Setting" : "Clearing", param2); } __initfunc(static void daring(int *ints,int param, int param2)) diff -u --recursive --new-file v2.2.11/linux/drivers/block/ide-pmac.c linux/drivers/block/ide-pmac.c --- v2.2.11/linux/drivers/block/ide-pmac.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/block/ide-pmac.c Wed Aug 25 17:29:46 1999 @@ -30,6 +30,7 @@ #ifdef CONFIG_PMAC_PBOOK #include #include +#include #endif #include "ide.h" #include "ide_modes.h" @@ -48,9 +49,9 @@ #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ #ifdef CONFIG_PMAC_PBOOK -static int idepmac_notify(struct notifier_block *, unsigned long, void *); -struct notifier_block idepmac_sleep_notifier = { - idepmac_notify +static int idepmac_notify(struct pmu_sleep_notifier *self, int when); +struct pmu_sleep_notifier idepmac_sleep_notifier = { + idepmac_notify, SLEEP_LEVEL_BLOCK, }; #endif /* CONFIG_PMAC_PBOOK */ @@ -220,7 +221,7 @@ pmac_ide_count = i; #ifdef CONFIG_PMAC_PBOOK - notifier_chain_register(&sleep_notifier_list, &idepmac_sleep_notifier); + pmu_register_sleep_notifier(&idepmac_sleep_notifier); #endif /* CONFIG_PMAC_PBOOK */ } @@ -380,29 +381,105 @@ #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ #ifdef CONFIG_PMAC_PBOOK -static int idepmac_notify(struct notifier_block *this, - unsigned long code, void *p) +static void idepmac_sleep_disk(int i, unsigned long base) { - int i, timeout; + int j; - switch (code) { - case PBOOK_SLEEP: - /* do anything here?? */ + /* Reset to PIO 0 */ + out_le32((unsigned *)(base + 0x200 + _IO_BASE), 0x2f8526); + + /* FIXME: We only handle the master IDE */ + if (ide_hwifs[i].drives[0].media == ide_disk) { + /* Spin down the drive */ + outb(0xa0, base+0x60); + outb(0x0, base+0x30); + outb(0x0, base+0x20); + outb(0x0, base+0x40); + outb(0x0, base+0x50); + outb(0xe0, base+0x70); + outb(0x2, base+0x160); + for (j = 0; j < 10; j++) { + int status; + mdelay(100); + status = inb(base+0x70); + if (!(status & BUSY_STAT) && (status & DRQ_STAT)) + break; + } + } +} + +static void idepmac_wake_disk(int i, unsigned long base) +{ + int j; + + /* Revive IDE disk and controller */ + feature_set(pmac_ide_node[i], FEATURE_IDE_enable); + mdelay(1); + feature_set(pmac_ide_node[i], FEATURE_IDE_DiskPower); + mdelay(100); + feature_set(pmac_ide_node[i], FEATURE_IDE_Reset); + mdelay(1); + /* Make sure we are still PIO0 */ + out_le32((unsigned *)(base + 0x200 + _IO_BASE), 0x2f8526); + mdelay(100); + + /* Wait up to 10 seconds (enough for recent drives) */ + for (j = 0; j < 100; j++) { + int status; + mdelay(100); + status = inb(base + 0x70); + if (!(status & BUSY_STAT)) + break; + } +} + +/* Here we handle media bay devices */ +static void +idepmac_wake_bay(int i, unsigned long base) +{ + int timeout; + + timeout = 5000; + while ((inb(base + 0x70) & BUSY_STAT) && timeout) { + mdelay(1); + --timeout; + } +} + +static int idepmac_notify(struct pmu_sleep_notifier *self, int when) +{ + int i, ret; + unsigned long base; + + switch (when) { + case PBOOK_SLEEP_REQUEST: + break; + case PBOOK_SLEEP_REJECT: + break; + case PBOOK_SLEEP_NOW: + for (i = 0; i < pmac_ide_count; ++i) { + if ((base = pmac_ide_regbase[i]) == 0) + continue; + /* Disable irq during sleep */ + disable_irq(pmac_ide_irq[i]); + /* Put the disk to sleep */ + idepmac_sleep_disk(i, base); + } break; case PBOOK_WAKE: - /* wait for the controller(s) to become ready */ - timeout = 5000; for (i = 0; i < pmac_ide_count; ++i) { - unsigned long base = pmac_ide_regbase[i]; - if (check_media_bay_by_base(base, MB_CD) == -EINVAL) + if ((base = pmac_ide_regbase[i]) == 0) continue; - while ((inb(base + 0x70) & BUSY_STAT) && timeout) { - mdelay(1); - --timeout; - } + /* We don't handle media bay devices this way */ + ret = check_media_bay_by_base(base, MB_CD); + if (ret == -ENODEV) + idepmac_wake_disk(i, base); + else if (ret == 0) + idepmac_wake_bay(i, base); + enable_irq(pmac_ide_irq[i]); } break; } - return NOTIFY_DONE; + return PBOOK_SLEEP_OK; } #endif /* CONFIG_PMAC_PBOOK */ diff -u --recursive --new-file v2.2.11/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c --- v2.2.11/linux/drivers/cdrom/sonycd535.c Thu Jan 7 08:46:59 1999 +++ linux/drivers/cdrom/sonycd535.c Wed Aug 25 17:29:46 1999 @@ -1524,9 +1524,11 @@ printk(CDU535_MESSAGE_NAME ": my base address is not free!\n"); return -EIO; } + /* look for the CD-ROM, follows the procedure in the DOS driver */ inb(select_unit_reg); /* wait for 40 18 Hz ticks (reverse-engineered from DOS driver) */ + current->state = TASK_INTERRUPTIBLE; schedule_timeout((HZ+17)*40/18); inb(result_reg); diff -u --recursive --new-file v2.2.11/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.2.11/linux/drivers/char/Config.in Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/Config.in Wed Aug 25 17:29:46 1999 @@ -37,6 +37,7 @@ tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION fi tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8 + tristate 'Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX if [ "$CONFIG_SPECIALIX" != "n" ]; then bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS @@ -168,6 +169,9 @@ if [ "$CONFIG_RADIO_ZOLTRIX" = "y" ]; then hex ' ZOLTRIX I/O port (0x20c or 0x30c)' CONFIG_RADIO_ZOLTRIX_PORT 20c fi + dep_tristate 'Zoran ZR36057/36060 support' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV + dep_tristate ' Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN + #dep_tristate ' Include support for LML33' CONFIG_VIDEO_LML33 $CONFIG_VIDEO_ZORAN fi endmenu diff -u --recursive --new-file v2.2.11/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.2.11/linux/drivers/char/Makefile Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/Makefile Wed Aug 25 17:29:46 1999 @@ -132,6 +132,14 @@ endif endif +ifeq ($(CONFIG_COMPUTONE),y) +L_OBJS += ip2.o ip2main.o +else + ifeq ($(CONFIG_COMPUTONE),m) + M_OBJS += ip2.o ip2main.o + endif +endif + ifeq ($(CONFIG_RISCOM8),y) L_OBJS += riscom8.o else @@ -367,6 +375,30 @@ else ifeq ($(CONFIG_VIDEO_CQCAM),m) M_OBJS += c-qcam.o + endif +endif + +ifeq ($(CONFIG_VIDEO_ZORAN),y) +L_OBJS += buz.o +else + ifeq ($(CONFIG_VIDEO_ZORAN),m) + M_OBJS += buz.o + endif +endif + +ifeq ($(CONFIG_VIDEO_LML33),y) +L_OBJS += bt856.o bt819.o +else + ifeq ($(CONFIG_VIDEO_LML33),m) + M_OBJS += bt856.o bt819.o + endif +endif + +ifeq ($(CONFIG_VIDEO_BUZ),y) +L_OBJS += saa7111.o saa7185.o +else + ifeq ($(CONFIG_VIDEO_BUZ),m) + M_OBJS += saa7111.o saa7185.o endif endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/README.computone linux/drivers/char/README.computone --- v2.2.11/linux/drivers/char/README.computone Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/README.computone Wed Aug 25 17:29:46 1999 @@ -0,0 +1,195 @@ + +Computone Intelliport II/Plus Multiport Serial Driver +----------------------------------------------------- + +Release Notes For Linux Kernel 2.2 +These notes have been tested on Linux kernels 2.0 and 2.2. + + +Version: 1.2.4 +Date: 08/04/99 +Fixes and Updates: Doug McNash +Historical Author: Andrew Manison + +1. INTRODUCTION + +This driver supports the entire family of Intelliport II/Plus controllers +with the exception of the MicroChannel controllers. + +This driver was developed on the v2.0.x Linux source tree and has been +tested up to v2.2.10; it will probably not work with earlier v1.X kernels, +and has not yet been tested on the v2.1.x tree. The most likely problems +will be in patching the kernel sources to support the driver. For this +reason there are 2 different patch files for 2.0.XX and 2.2.XX kernels. +Make sure you use the right one! +Someday soon it should be included in the 2.3.XX tree. + +2. QUICK INSTALLATION + +Hardware - If you have an ISA card, find a free interrupt and io port. + List those in use with `cat /proc/interrupts` and + `cat /proc/ioports`. Set the card dip switches to that free + address. You may need to configure your BIOS to reserve the + irq for the ISA card. PCI and EISA parameters are set + automagically and need no attention. Insert card into + computer with the power off before or after driver installation. + +Software - + +Module installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (m) module for CONFIG_COMPUTONE under character + devices. CONFIG_PCI and CONFIG_MODULES also may need to be set. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h if needed + or + edit /etc/conf.modules (or /etc/modules.conf) if needed (module). + or both to match this setting. +f) Run "make dep" +g) Run "make modules" +h) Run "make modules_install" +i) Run "/sbin/depmod -a" +i) install driver using `modprobe ip2 ` (options listed below) +j) run mkip2dev + + +Kernel installation: + +a) Obtain driver-kernel patch file +b) Copy to the linux source tree root, Run ip2build (if not patch) +c) Determine free irq/address to use if any (configure BIOS if need be) +d) Run "make config" or "make menuconfig" or "make xconfig" + Select (y) kernel for CONFIG_COMPUTONE under character + devices. CONFIG_PCI may need to be set if you have PCI bus. +e) Set address on ISA cards then: + edit /usr/src/linux/drivers/char/ip2/ip2.h +f) Run "make dep" +g) Run "make zImage" or whatever target you prefer. +h) mv /usr/src/linux/arch/i386/boot/zImage to /boot. +i) add new config for this kernel into /etc/lilo.conf, run "lilo" +j) reboot using this kernel +k) make and run ip2/mkip2dev + +3. INSTALLATION + +Previously, the driver sources were packaged with a set of patch files +to update the character drivers' makefile and configuration file, and other +kernel source files. A build script (ip2build) was included which applies +the patches if needed, and build any utilities needed. +What you recieve may be a single patch file in conventional kernel +patch format build script. That form can also be applied by +running patch -p1 < ThePatchFile. Otherwise run ip2build. + +The driver can be installed as a module (recommended) or built into the +kernel. This is selected as for other drivers through the `make config` +command from the root of the Linux source tree. If the driver is built +into the kernel you will need to edit the file ip2.h to match the boards +you are installing. See that file for instructions. If the driver is +installed as a module the configuration can also be specified on the +modprobe command line as follows: + + modprobe ip2 irq=irq1,irq2,irq3,irq4 io=addr1,addr2,addr3,addr4 + +where irqnum is one of the valid Intelliport II interrupts (3,4,5,7,10,11, +12,15) and addr1-4 are the base addresses for up to four controllers. If +the irqs are not specified the driver uses the default in ip2/ip2.h (which +selects polled mode). If no base addresses are specified the defaults in +ip2.h are used. If you are autoloading the driver module with kerneld or +kmod the base addresses and interrupt number must also be set in ip2/ip2.h +and recompile or just insert and options line in /etc/modules.conf or both. +The options line is equivalent to the command line and takes precidence over +what is in ip2.h. + +/etc/modules.conf sample: + options ip2 io=1,0x328 irq=1,10 + alias char-major-71 ip2 + alias char-major-72 ip2 + alias char-major-73 ip2 + +equivelant ip2.h: +static ip2config_t ip2config = +{ + {1,10,0,0}, + { + 0x0001, // Board 0, ttyF0 - ttyF63 /* PCI card */ + 0x0328, // Board 1, ttyF64 - ttyF127 /* ISA card */ + 0x0000, // Board 2, ttyF128 - ttyF191 /* empty */ + 0x0000 // Board 3, ttyF192 - ttyF255 /* empty */ + } +}; + +Specifying an invalid or in-use irq will default the driver into +running in polled mode for that card. If all irq entries are 0 then +all cards will operate in polled mode. + +Tarball Install: + +The whole tarfile should be untarred in the /usr/src/linux/drivers/char/ +directory. Most files required for the driver are placed in the ip2 +subdirectory. Then execute the script + + ip2build + +which will patch the files. + +Kernel Patch Install: + + cd to the Linux source root, run patch -p1 < ThePatchFile. + +Now return to the root directory of the Linux +source tree and run make config or make menuconfig. You will be prompted +for the Computone drivers, either as a module or part of the kernel. +If you have a PCI card you many need to select PCI bios support (CONFIG_PCI) +if not enabled already. Ditto for CONFIG_MODULES if you use modules. + +If you select the driver as part of the kernel run : + + make depend + make zlilo (or whatever you do to create a bootable kernel) + +If you selected a module run : + + make modules && make modules_install + +The utility ip2mkdev creates all the device nodes required by the driver. +For a device to be created it must be configured in the driver and the +board must be installed. Only devices corresponding to real IntelliPort II +ports are created. With multiple boards and expansion boxes this will +leave gaps in the sequence of device names. ip2mkdev uses Linux tty naming +conventions: ttyF0 - ttyF255 for normal devices, and cuf0 - cuf255 for +callout devices. + +4. USING THE DRIVERS + +As noted above, the driver implements the ports in accordance with Linux +conventions, and the devices should be interchangeable with the standard +serial devices. (This is a key point for problem reporting: please make +sure that what you are trying do works on the ttySx/cuax ports first; then +tell us what went wrong with the ip2 ports!) + +Higher speeds can be obtained using the setserial utility which remaps +38,400 bps (extb) to 57,600 bps, 115,200 bps, or a custom speed. +Intelliport II installations using the PowerPort expansion module can +use the custom speed setting to select the highest speeds: 153,600 bps, +230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for +custom baud rate configuration is fixed at 921,600 for cards/expantion +modules with ST654's and 115200 for those with Cirrus CD1400's. This +corresponds to the maximum bit rates those chips are capable. +For example if the baud base is 921600 and the baud divisor is 18 then +the custom rate is 921600/18 = 51200 bps. See the setserial man page for +complete details. Of course if stty accepts the higher rates now you can +use that as well as the standard ioctls(). + +5. NOTES + +This is a release version of the driver, but it is impossible to test it +in all configurations of Linux. If there is any anomalous behaviour that +does not match the standard serial port's behaviour please let us know. + +Author: dmcnash@computine.com +Testing: larryg@computone.com +Spport: support@computone.com diff -u --recursive --new-file v2.2.11/linux/drivers/char/bttv.c linux/drivers/char/bttv.c --- v2.2.11/linux/drivers/char/bttv.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/bttv.c Wed Aug 25 17:29:47 1999 @@ -1471,10 +1471,10 @@ /* This doesn´t work like this for NTSC anyway. So, better check the total image size ... */ -/* - if(mp->height>576 || mp->width>768+BURSTOFFSET) + + if(mp->height>576 || mp->width>768+BURSTOFFSET || mp->height < 32 || mp->width <32) return -EINVAL; -*/ + if (mp->format >= PALETTEFMT_MAX) return -EINVAL; if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2 @@ -1977,7 +1977,8 @@ { struct video_buffer v; #if LINUX_VERSION_CODE >= 0x020100 - if(!capable(CAP_SYS_ADMIN)) + if(!capable(CAP_SYS_ADMIN) + || !capable(CAP_SYS_RAWIO)) #else if(!suser()) #endif @@ -1989,12 +1990,7 @@ v.height > 16 && v.bytesperline > 16) return -EINVAL; if (v.base) - { - if ((unsigned long)v.base&1) - btv->win.vidadr=(unsigned long)(PAGE_OFFSET|uvirt_to_bus((unsigned long)v.base)); - else - btv->win.vidadr=(unsigned long)v.base; - } + btv->win.vidadr=(unsigned long)v.base; btv->win.sheight=v.height; btv->win.swidth=v.width; btv->win.bpp=((v.depth+7)&0x38)/8; @@ -2216,6 +2212,8 @@ struct video_mmap vm; if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) return -EFAULT; + if (vm.frame < 0 || vm.frame >= MAX_GBUFFERS) + return -EIO; if (btv->frame_stat[vm.frame] == GBUFFER_GRABBING) return -EBUSY; return vgrab(btv, &vm); diff -u --recursive --new-file v2.2.11/linux/drivers/char/buz.c linux/drivers/char/buz.c --- v2.2.11/linux/drivers/char/buz.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/buz.c Wed Aug 25 17:29:47 1999 @@ -0,0 +1,3482 @@ +#define MAX_KMALLOC_MEM (512*1024) +/* + buz - Iomega Buz driver version 1.0 + + Copyright (C) 1999 Rainer Johanni + + based on + + buz.0.0.3 Copyright (C) 1998 Dave Perks + + and + + bttv - Bt848 frame grabber driver + + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include "buz.h" +#include +#include + +#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | /* ZR36057_ISR_GIRQ1 | ZR36057_ISR_CodRepIRQ | */ ZR36057_ISR_JPEGRepIRQ ) +#define GPIO_MASK 0xdf + +/* + + BUZ + + GPIO0 = 1, take board out of reset + GPIO1 = 1, take JPEG codec out of sleep mode + GPIO3 = 1, deassert FRAME# to 36060 + + + GIRQ0 signals a vertical sync of the video signal + GIRQ1 signals that ZR36060's DATERR# line is asserted. + + SAA7111A + + In their infinite wisdom, the Iomega engineers decided to + use the same input line for composite and S-Video Color, + although there are two entries not connected at all! + Through this ingenious strike, it is not possible to + keep two running video sources connected at the same time + to Composite and S-VHS input! + + mode 0 - N/C + mode 1 - S-Video Y + mode 2 - noise or something I don't know + mode 3 - Composite and S-Video C + mode 4 - N/C + mode 5 - S-Video (gain C independently selectable of gain Y) + mode 6 - N/C + mode 7 - S-Video (gain C adapted to gain Y) + */ + +#define MAJOR_VERSION 1 /* driver major version */ +#define MINOR_VERSION 0 /* driver minor version */ + +#define BUZ_NAME "Iomega BUZ V-1.0" /* name of the driver */ + +#define DEBUG(x) /* Debug driver */ +#define IDEBUG(x) /* Debug interrupt handler */ +#define IOCTL_DEBUG(x) + + +/* The parameters for this driver */ + +/* + The video mem address of the video card. + The driver has a little database for some videocards + to determine it from there. If your video card is not in there + you have either to give it to the driver as a parameter + or set in in a VIDIOCSFBUF ioctl + */ + +static unsigned long vidmem = 0; /* Video memory base address */ + +/* Special purposes only: */ + +static int triton = 0; /* 0=no, 1=yes */ +static int natoma = 0; /* 0=no, 1=yes */ + +/* + Number and size of grab buffers for Video 4 Linux + The vast majority of applications should not need more than 2, + the very popular BTTV driver actually does ONLY have 2. + Time sensitive applications might need more, the maximum + is VIDEO_MAX_FRAME (defined in ). + + The size is set so that the maximum possible request + can be satisfied. Decrease it, if bigphys_area alloc'd + memory is low. If you don't have the bigphys_area patch, + set it to 128 KB. Will you allow only to grab small + images with V4L, but that's better than nothing. + + v4l_bufsize has to be given in KB ! + + */ + +static int v4l_nbufs = 2; +static int v4l_bufsize = 128; /* Everybody should be able to work with this setting */ + +/* + Default input and video norm at startup of the driver. + */ + +static int default_input = 0; /* 0=Composite, 1=S-VHS */ +static int default_norm = 0; /* 0=PAL, 1=NTSC */ + +MODULE_PARM(vidmem, "i"); +MODULE_PARM(triton, "i"); +MODULE_PARM(natoma, "i"); +MODULE_PARM(v4l_nbufs, "i"); +MODULE_PARM(v4l_bufsize, "i"); +MODULE_PARM(default_input, "i"); +MODULE_PARM(default_norm, "i"); + +/* Anybody who uses more than four? */ +#define BUZ_MAX 4 + +static int zoran_num; /* number of Buzs in use */ +static struct zoran zoran[BUZ_MAX]; + +/* forward references */ + +static void v4l_fbuffer_free(struct zoran *zr); +static void jpg_fbuffer_free(struct zoran *zr); +static void zoran_feed_stat_com(struct zoran *zr); + + + +/* + * Allocate the V4L grab buffers + * + * These have to be pysically contiguous. + * If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc + */ + +static int v4l_fbuffer_alloc(struct zoran *zr) +{ + int i, off; + unsigned char *mem; + + for (i = 0; i < v4l_nbufs; i++) { + if (zr->v4l_gbuf[i].fbuffer) + printk(KERN_WARNING "%s: v4l_fbuffer_alloc: buffer %d allready allocated ?\n", zr->name, i); + + if (v4l_bufsize <= MAX_KMALLOC_MEM) { + /* Use kmalloc */ + + mem = (unsigned char *) kmalloc(v4l_bufsize, GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR "%s: kmalloc for V4L bufs failed\n", zr->name); + v4l_fbuffer_free(zr); + return -ENOBUFS; + } + zr->v4l_gbuf[i].fbuffer = mem; + zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem); + zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem); + for (off = 0; off < v4l_bufsize; off += PAGE_SIZE) + mem_map_reserve(MAP_NR(mem + off)); + DEBUG(printk(BUZ_INFO ": V4L frame %d mem 0x%x (bus: 0x%x=%d)\n", i, mem, virt_to_bus(mem), virt_to_bus(mem))); + } else { + return -ENOBUFS; + } + } + + return 0; +} + +/* free the V4L grab buffers */ +static void v4l_fbuffer_free(struct zoran *zr) +{ + int i, off; + unsigned char *mem; + + for (i = 0; i < v4l_nbufs; i++) { + if (!zr->v4l_gbuf[i].fbuffer) + continue; + + mem = zr->v4l_gbuf[i].fbuffer; + for (off = 0; off < v4l_bufsize; off += PAGE_SIZE) + mem_map_unreserve(MAP_NR(mem + off)); + kfree((void *) zr->v4l_gbuf[i].fbuffer); + zr->v4l_gbuf[i].fbuffer = NULL; + } +} + +/* + * Allocate the MJPEG grab buffers. + * + * If the requested buffer size is smaller than MAX_KMALLOC_MEM, + * kmalloc is used to request a physically contiguous area, + * else we allocate the memory in framgents with get_free_page. + * + * If a Natoma chipset is present and this is a revision 1 zr36057, + * each MJPEG buffer needs to be physically contiguous. + * (RJ: This statement is from Dave Perks' original driver, + * I could never check it because I have a zr36067) + * The driver cares about this because it reduces the buffer + * size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation). + * + * RJ: The contents grab buffers needs never be accessed in the driver. + * Therefore there is no need to allocate them with vmalloc in order + * to get a contiguous virtual memory space. + * I don't understand why many other drivers first allocate them with + * vmalloc (which uses internally also get_free_page, but delivers you + * virtual addresses) and then again have to make a lot of efforts + * to get the physical address. + * + */ + +static int jpg_fbuffer_alloc(struct zoran *zr) +{ + int i, j, off, alloc_contig; + unsigned long mem; + + /* Decide if we should alloc contiguous or fragmented memory */ + /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */ + + alloc_contig = (zr->jpg_bufsize < MAX_KMALLOC_MEM); + + for (i = 0; i < zr->jpg_nbufs; i++) { + if (zr->jpg_gbuf[i].frag_tab) + printk(KERN_WARNING "%s: jpg_fbuffer_alloc: buffer %d allready allocated ???\n", zr->name, i); + + /* Allocate fragment table for this buffer */ + + mem = get_free_page(GFP_KERNEL); + if (mem == 0) { + printk(KERN_ERR "%s: jpg_fbuffer_alloc: get_free_page (frag_tab) failed for buffer %d\n", zr->name, i); + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + memset((void *) mem, 0, PAGE_SIZE); + zr->jpg_gbuf[i].frag_tab = (u32 *) mem; + zr->jpg_gbuf[i].frag_tab_bus = virt_to_bus((void *) mem); + + if (alloc_contig) { + mem = (unsigned long) kmalloc(zr->jpg_bufsize, GFP_KERNEL); + if (mem == 0) { + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + zr->jpg_gbuf[i].frag_tab[0] = virt_to_bus((void *) mem); + zr->jpg_gbuf[i].frag_tab[1] = ((zr->jpg_bufsize / 4) << 1) | 1; + for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE) + mem_map_reserve(MAP_NR(mem + off)); + } else { + /* jpg_bufsize is alreay page aligned */ + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { + mem = get_free_page(GFP_KERNEL); + if (mem == 0) { + jpg_fbuffer_free(zr); + return -ENOBUFS; + } + zr->jpg_gbuf[i].frag_tab[2 * j] = virt_to_bus((void *) mem); + zr->jpg_gbuf[i].frag_tab[2 * j + 1] = (PAGE_SIZE / 4) << 1; + mem_map_reserve(MAP_NR(mem)); + } + + zr->jpg_gbuf[i].frag_tab[2 * j - 1] |= 1; + } + } + + DEBUG(printk("jpg_fbuffer_alloc: %d KB allocated\n", + (zr->jpg_nbufs * zr->jpg_bufsize) >> 10)); + zr->jpg_buffers_allocated = 1; + return 0; +} + +/* free the MJPEG grab buffers */ +static void jpg_fbuffer_free(struct zoran *zr) +{ + int i, j, off, alloc_contig; + unsigned char *mem; + + /* Decide if we should alloc contiguous or fragmented memory */ + /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */ + + alloc_contig = (zr->jpg_bufsize < MAX_KMALLOC_MEM); + + for (i = 0; i < zr->jpg_nbufs; i++) { + if (!zr->jpg_gbuf[i].frag_tab) + continue; + + if (alloc_contig) { + if (zr->jpg_gbuf[i].frag_tab[0]) { + mem = (unsigned char *) bus_to_virt(zr->jpg_gbuf[i].frag_tab[0]); + for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE) + mem_map_unreserve(MAP_NR(mem + off)); + kfree((void *) mem); + zr->jpg_gbuf[i].frag_tab[0] = 0; + zr->jpg_gbuf[i].frag_tab[1] = 0; + } + } else { + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { + if (!zr->jpg_gbuf[i].frag_tab[2 * j]) + break; + mem_map_unreserve(MAP_NR(bus_to_virt(zr->jpg_gbuf[i].frag_tab[2 * j]))); + free_page((unsigned long) bus_to_virt(zr->jpg_gbuf[i].frag_tab[2 * j])); + zr->jpg_gbuf[i].frag_tab[2 * j] = 0; + zr->jpg_gbuf[i].frag_tab[2 * j + 1] = 0; + } + } + + free_page((unsigned long) zr->jpg_gbuf[i].frag_tab); + zr->jpg_gbuf[i].frag_tab = NULL; + } + zr->jpg_buffers_allocated = 0; +} + + +/* ----------------------------------------------------------------------- */ + +/* I2C functions */ + +#define I2C_DELAY 10 + + +/* software I2C functions */ + +static void i2c_setlines(struct i2c_bus *bus, int ctrl, int data) +{ + struct zoran *zr = (struct zoran *) bus->data; + btwrite((data << 1) | ctrl, ZR36057_I2CBR); + btread(ZR36057_I2CBR); + udelay(I2C_DELAY); +} + +static int i2c_getdataline(struct i2c_bus *bus) +{ + struct zoran *zr = (struct zoran *) bus->data; + return (btread(ZR36057_I2CBR) >> 1) & 1; +} + +void attach_inform(struct i2c_bus *bus, int id) +{ + DEBUG(struct zoran *zr = (struct zoran *) bus->data); + DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id)); +} + +void detach_inform(struct i2c_bus *bus, int id) +{ + DEBUG(struct zoran *zr = (struct zoran *) bus->data); + DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id)); +} + +static struct i2c_bus zoran_i2c_bus_template = +{ + "zr36057", + I2C_BUSID_BT848, + NULL, + + SPIN_LOCK_UNLOCKED, + + attach_inform, + detach_inform, + + i2c_setlines, + i2c_getdataline, + NULL, + NULL, +}; + + +/* ----------------------------------------------------------------------- */ + +static void GPIO(struct zoran *zr, unsigned bit, unsigned value) +{ + u32 reg; + u32 mask; + + mask = 1 << (24 + bit); + reg = btread(ZR36057_GPPGCR1) & ~mask; + if (value) { + reg |= mask; + } + btwrite(reg, ZR36057_GPPGCR1); + /* Stop any PCI posting on the GPIO bus */ + btread(ZR36057_I2CBR); +} + + +/* + * Set the registers for the size we have specified. Don't bother + * trying to understand this without the ZR36057 manual in front of + * you [AC]. + * + * PS: The manual is free for download in .pdf format from + * www.zoran.com - nicely done those folks. + */ + +struct tvnorm { + u16 Wt, Wa, Ht, Ha, HStart, VStart; +}; + +static struct tvnorm tvnorms[] = +{ + /* PAL-BDGHI */ + {864, 720, 625, 576, 31, 16}, + /* NTSC */ + {858, 720, 525, 480, 21, 8}, +}; +#define TVNORMS (sizeof(tvnorms) / sizeof(tvnorm)) + +static int format2bpp(int format) +{ + int bpp; + + /* Determine the number of bytes per pixel for the video format requested */ + + switch (format) { + + case VIDEO_PALETTE_YUV422: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB555: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB565: + bpp = 2; + break; + + case VIDEO_PALETTE_RGB24: + bpp = 3; + break; + + case VIDEO_PALETTE_RGB32: + bpp = 4; + break; + + default: + bpp = 0; + } + + return bpp; +} + +/* + * set geometry + */ +static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, + unsigned int video_format) +{ + struct tvnorm *tvn; + unsigned HStart, HEnd, VStart, VEnd; + unsigned DispMode; + unsigned VidWinWid, VidWinHt; + unsigned hcrop1, hcrop2, vcrop1, vcrop2; + unsigned Wa, We, Ha, He; + unsigned X, Y, HorDcm, VerDcm; + u32 reg; + unsigned mask_line_size; + + if (zr->params.norm < 0 || zr->params.norm > 1) { + printk(KERN_ERR "%s: set_vfe: video_norm = %d not valid\n", zr->name, zr->params.norm); + return; + } + if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT) { + printk(KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", zr->name, video_width, video_height); + return; + } + tvn = &tvnorms[zr->params.norm]; + + Wa = tvn->Wa; + Ha = tvn->Ha; + + /* if window has more than half of active height, + switch on interlacing - we want the full information */ + + zr->video_interlace = (video_height > Ha / 2); + +/**** zr36057 ****/ + + /* horizontal */ + VidWinWid = video_width; + X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa; + We = (VidWinWid * 64) / X; + HorDcm = 64 - X; + hcrop1 = 2 * ((tvn->Wa - We) / 4); + hcrop2 = tvn->Wa - We - hcrop1; + HStart = tvn->HStart | 1; + HEnd = HStart + tvn->Wa - 1; + HStart += hcrop1; + HEnd -= hcrop2; + reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart) + | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd); + reg |= ZR36057_VFEHCR_HSPol; + btwrite(reg, ZR36057_VFEHCR); + + /* Vertical */ + DispMode = !zr->video_interlace; + VidWinHt = DispMode ? video_height : video_height / 2; + Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha; + He = (VidWinHt * 64) / Y; + VerDcm = 64 - Y; + vcrop1 = (tvn->Ha / 2 - He) / 2; + vcrop2 = tvn->Ha / 2 - He - vcrop1; + VStart = tvn->VStart; + VEnd = VStart + tvn->Ha / 2 - 1; + VStart += vcrop1; + VEnd -= vcrop2; + reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart) + | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd); + reg |= ZR36057_VFEVCR_VSPol; + btwrite(reg, ZR36057_VFEVCR); + + /* scaler and pixel format */ + reg = 0 // ZR36057_VFESPFR_ExtFl /* Trying to live without ExtFl */ + | (HorDcm << ZR36057_VFESPFR_HorDcm) + | (VerDcm << ZR36057_VFESPFR_VerDcm) + | (DispMode << ZR36057_VFESPFR_DispMode) + | ZR36057_VFESPFR_LittleEndian; + /* RJ: I don't know, why the following has to be the opposite + of the corresponding ZR36060 setting, but only this way + we get the correct colors when uncompressing to the screen */ + reg |= ZR36057_VFESPFR_VCLKPol; + /* RJ: Don't know if that is needed for NTSC also */ + reg |= ZR36057_VFESPFR_TopField; + switch (video_format) { + + case VIDEO_PALETTE_YUV422: + reg |= ZR36057_VFESPFR_YUV422; + break; + + case VIDEO_PALETTE_RGB555: + reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif; + break; + + case VIDEO_PALETTE_RGB565: + reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif; + break; + + case VIDEO_PALETTE_RGB24: + reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24; + break; + + case VIDEO_PALETTE_RGB32: + reg |= ZR36057_VFESPFR_RGB888; + break; + + default: + printk(KERN_INFO "%s: Unknown color_fmt=%x\n", zr->name, video_format); + return; + + } + if (HorDcm >= 48) { + reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */ + } else if (HorDcm >= 32) { + reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */ + } else if (HorDcm >= 16) { + reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */ + } + btwrite(reg, ZR36057_VFESPFR); + + /* display configuration */ + + reg = (16 << ZR36057_VDCR_MinPix) + | (VidWinHt << ZR36057_VDCR_VidWinHt) + | (VidWinWid << ZR36057_VDCR_VidWinWid); + if (triton) + reg &= ~ZR36057_VDCR_Triton; + else + reg |= ZR36057_VDCR_Triton; + btwrite(reg, ZR36057_VDCR); + + /* Write overlay clipping mask data, but don't enable overlay clipping */ + /* RJ: since this makes only sense on the screen, we use + zr->window.width instead of video_width */ + + mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; + reg = virt_to_bus(zr->overlay_mask); + btwrite(reg, ZR36057_MMTR); + reg = virt_to_bus(zr->overlay_mask + mask_line_size); + btwrite(reg, ZR36057_MMBR); + reg = mask_line_size - (zr->window.width + 31) / 32; + if (DispMode == 0) + reg += mask_line_size; + reg <<= ZR36057_OCR_MaskStride; + btwrite(reg, ZR36057_OCR); + +} + +/* + * Switch overlay on or off + */ + +static void zr36057_overlay(struct zoran *zr, int on) +{ + int fmt, bpp; + u32 reg; + + if (on) { + /* do the necessary settings ... */ + + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */ + + switch (zr->buffer.depth) { + case 15: + fmt = VIDEO_PALETTE_RGB555; + bpp = 2; + break; + case 16: + fmt = VIDEO_PALETTE_RGB565; + bpp = 2; + break; + case 24: + fmt = VIDEO_PALETTE_RGB24; + bpp = 3; + break; + case 32: + fmt = VIDEO_PALETTE_RGB32; + bpp = 4; + break; + default: + fmt = 0; + bpp = 0; + } + + zr36057_set_vfe(zr, zr->window.width, zr->window.height, fmt); + + /* Start and length of each line MUST be 4-byte aligned. + This should be allready checked before the call to this routine. + All error messages are internal driver checking only! */ + + /* video display top and bottom registers */ + + reg = (u32) zr->buffer.base + + zr->window.x * bpp + + zr->window.y * zr->buffer.bytesperline; + btwrite(reg, ZR36057_VDTR); + if (reg & 3) + printk(KERN_ERR "%s: zr36057_overlay: video_address not aligned\n", zr->name); + if (zr->video_interlace) + reg += zr->buffer.bytesperline; + btwrite(reg, ZR36057_VDBR); + + /* video stride, status, and frame grab register */ + + reg = zr->buffer.bytesperline - zr->window.width * bpp; + if (zr->video_interlace) + reg += zr->buffer.bytesperline; + if (reg & 3) + printk(KERN_ERR "%s: zr36057_overlay: video_stride not aligned\n", zr->name); + reg = (reg << ZR36057_VSSFGR_DispStride); + reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */ + btwrite(reg, ZR36057_VSSFGR); + + /* Set overlay clipping */ + + if (zr->window.clipcount) + btor(ZR36057_OCR_OvlEnable, ZR36057_OCR); + + /* ... and switch it on */ + + btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); + } else { + /* Switch it off */ + + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + } +} + +/* + * The overlay mask has one bit for each pixel on a scan line, + * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. + */ +static void write_overlay_mask(struct zoran *zr, struct video_clip *vp, int count) +{ + unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; + u32 *mask; + int x, y, width, height; + unsigned i, j, k; + u32 reg; + + /* fill mask with one bits */ + memset(zr->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); + reg = 0; + + for (i = 0; i < count; ++i) { + /* pick up local copy of clip */ + x = vp[i].x; + y = vp[i].y; + width = vp[i].width; + height = vp[i].height; + + /* trim clips that extend beyond the window */ + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + if (x + width > zr->window.width) { + width = zr->window.width - x; + } + if (y + height > zr->window.height) { + height = zr->window.height - y; + } + /* ignore degenerate clips */ + if (height <= 0) { + continue; + } + if (width <= 0) { + continue; + } + /* apply clip for each scan line */ + for (j = 0; j < height; ++j) { + /* reset bit for each pixel */ + /* this can be optimized later if need be */ + mask = zr->overlay_mask + (y + j) * mask_line_size; + for (k = 0; k < width; ++k) { + mask[(x + k) / 32] &= ~((u32) 1 << (x + k) % 32); + } + } + } +} + +/* Enable/Disable uncompressed memory grabbing of the 36057 */ + +static void zr36057_set_memgrab(struct zoran *zr, int mode) +{ + if (mode) { + if (btread(ZR36057_VSSFGR) & (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab)) + printk(KERN_WARNING "%s: zr36057_set_memgrab_on with SnapShot or FrameGrab on ???\n", zr->name); + + /* switch on VSync interrupts */ + + btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts + + btor(ZR36057_ICR_GIRQ0, ZR36057_ICR); + + /* enable SnapShot */ + + btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); + + /* Set zr36057 video front end and enable video */ + +#ifdef XAWTV_HACK + zr36057_set_vfe(zr, zr->gwidth > 720 ? 720 : zr->gwidth, zr->gheight, zr->gformat); +#else + zr36057_set_vfe(zr, zr->gwidth, zr->gheight, zr->gformat); +#endif + + zr->v4l_memgrab_active = 1; + } else { + zr->v4l_memgrab_active = 0; + + /* switch off VSync interrupts */ + + btand(~ZR36057_ICR_GIRQ0, ZR36057_ICR); + + /* reenable grabbing to screen if it was running */ + + if (zr->v4l_overlay_active) { + zr36057_overlay(zr, 1); + } else { + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); + } + } +} + +static int wait_grab_pending(struct zoran *zr) +{ + unsigned long flags; + + /* wait until all pending grabs are finished */ + + if (!zr->v4l_memgrab_active) + return 0; + + while (zr->v4l_pend_tail != zr->v4l_pend_head) { + interruptible_sleep_on(&zr->v4l_capq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + spin_lock_irqsave(&zr->lock, flags); + zr36057_set_memgrab(zr, 0); + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} + +/* + * V4L Buffer grabbing + */ + +static int v4l_grab(struct zoran *zr, struct video_mmap *mp) +{ + unsigned long flags; + int res, bpp; + + /* + * There is a long list of limitations to what is allowed to be grabbed + * We don't output error messages her, since some programs (e.g. xawtv) + * just try several settings to find out what is valid or not. + */ + + /* No grabbing outside the buffer range! */ + + if (mp->frame >= v4l_nbufs || mp->frame < 0) + return -EINVAL; + + /* Check size and format of the grab wanted */ + + if (mp->height < BUZ_MIN_HEIGHT || mp->width < BUZ_MIN_WIDTH) + return -EINVAL; + if (mp->height > BUZ_MAX_HEIGHT || mp->width > BUZ_MAX_WIDTH) + return -EINVAL; + + bpp = format2bpp(mp->format); + if (bpp == 0) + return -EINVAL; + + /* Check against available buffer size */ + + if (mp->height * mp->width * bpp > v4l_bufsize) + return -EINVAL; + + /* The video front end needs 4-byte alinged line sizes */ + + if ((bpp == 2 && (mp->width & 1)) || (bpp == 3 && (mp->width & 3))) + return -EINVAL; + + /* + * To minimize the time spent in the IRQ routine, we avoid setting up + * the video front end there. + * If this grab has different parameters from a running streaming capture + * we stop the streaming capture and start it over again. + */ + + if (zr->v4l_memgrab_active && + (zr->gwidth != mp->width || zr->gheight != mp->height || zr->gformat != mp->format)) { + res = wait_grab_pending(zr); + if (res) + return res; + } + zr->gwidth = mp->width; + zr->gheight = mp->height; + zr->gformat = mp->format; + zr->gbpl = bpp * zr->gwidth; + + + spin_lock_irqsave(&zr->lock, flags); + + /* make sure a grab isn't going on currently with this buffer */ + + switch (zr->v4l_gbuf[mp->frame].state) { + + default: + case BUZ_STATE_PEND: + res = -EBUSY; /* what are you doing? */ + break; + + case BUZ_STATE_USER: + case BUZ_STATE_DONE: + /* since there is at least one unused buffer there's room for at least one more pend[] entry */ + zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = mp->frame; + zr->v4l_gbuf[mp->frame].state = BUZ_STATE_PEND; + res = 0; + break; + + } + + /* put the 36057 into frame grabbing mode */ + + if (!res && !zr->v4l_memgrab_active) + zr36057_set_memgrab(zr, 1); + + spin_unlock_irqrestore(&zr->lock, flags); + + return res; +} + +/* + * Sync on a V4L buffer + */ + +static int v4l_sync(struct zoran *zr, int frame) +{ + unsigned long flags; + + + /* check passed-in frame number */ + if (frame >= v4l_nbufs || frame < 0) { + printk(KERN_ERR "%s: v4l_sync: frame %d is invalid\n", zr->name, frame); + return -EINVAL; + } + /* Check if is buffer was queued at all */ + + if (zr->v4l_gbuf[frame].state == BUZ_STATE_USER) { +// printk(KERN_ERR "%s: v4l_sync: Trying to sync on a buffer which was not queued?\n", zr->name); + return -EINVAL; + } + /* wait on this buffer to get ready */ + + while (zr->v4l_gbuf[frame].state == BUZ_STATE_PEND) { + interruptible_sleep_on(&zr->v4l_capq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + /* buffer should now be in BUZ_STATE_DONE */ + + if (zr->v4l_gbuf[frame].state != BUZ_STATE_DONE) + printk(KERN_ERR "%s: v4l_sync - internal error\n", zr->name); + + /* Check if streaming capture has finished */ + + spin_lock_irqsave(&zr->lock, flags); + + if (zr->v4l_pend_tail == zr->v4l_pend_head) + zr36057_set_memgrab(zr, 0); + + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} +/***************************************************************************** + * * + * Set up the Buz-specific MJPEG part * + * * + *****************************************************************************/ + +/* + * Wait til post office is no longer busy + */ + +static int post_office_wait(struct zoran *zr) +{ + u32 por; + u32 ct=0; + + while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) { + ct++; + if(ct>100000) + { + printk(KERN_ERR "%s: timeout on post office.\n", zr->name); + return -1; + } + /* wait for something to happen */ + } + if ((por & ZR36057_POR_POPen) != 0) { + printk(KERN_WARNING "%s: pop pending %08x\n", zr->name, por); + return -1; + } +#if 0 + /* The LML 33 gets this bit wrong */ + + if ((por & (ZR36057_POR_POTime | ZR36057_POR_POPen)) != 0) { + printk(KERN_WARNING "%s: pop timeout %08x\n", zr->name, por); + return -1; + } +#endif + return 0; +} + +static int post_office_write(struct zoran *zr, unsigned guest, unsigned reg, unsigned value) +{ + u32 por; + + post_office_wait(zr); + por = ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16) | (value & 0xFF); + btwrite(por, ZR36057_POR); + return post_office_wait(zr); +} + +static int post_office_read(struct zoran *zr, unsigned guest, unsigned reg) +{ + u32 por; + + post_office_wait(zr); + por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16); + btwrite(por, ZR36057_POR); + if (post_office_wait(zr) < 0) { + return -1; + } + return btread(ZR36057_POR) & 0xFF; +} + +static int zr36060_write_8(struct zoran *zr, unsigned reg, unsigned val) +{ + if (post_office_wait(zr) + || post_office_write(zr, 0, 1, reg >> 8) + || post_office_write(zr, 0, 2, reg)) { + return -1; + } + return post_office_write(zr, 0, 3, val); +} + +static int zr36060_write_16(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_8(zr, reg + 0, val >> 8)) { + return -1; + } + return zr36060_write_8(zr, reg + 1, val >> 0); +} + +static int zr36060_write_24(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_8(zr, reg + 0, val >> 16)) { + return -1; + } + return zr36060_write_16(zr, reg + 1, val >> 0); +} + +static int zr36060_write_32(struct zoran *zr, unsigned reg, unsigned val) +{ + if (zr36060_write_16(zr, reg + 0, val >> 16)) { + return -1; + } + return zr36060_write_16(zr, reg + 2, val >> 0); +} + +static u32 zr36060_read_8(struct zoran *zr, unsigned reg) +{ + if (post_office_wait(zr) + || post_office_write(zr, 0, 1, reg >> 8) + || post_office_write(zr, 0, 2, reg)) { + return -1; + } + return post_office_read(zr, 0, 3) & 0xFF; +} + +static int zr36060_reset(struct zoran *zr) +{ + return post_office_write(zr, 3, 0, 0); +} + +static void zr36060_sleep(struct zoran *zr, int sleep) +{ + GPIO(zr, 1, !sleep); +} + + +static void zr36060_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + int size; + + reg = (1 << 0) /* CodeMstr */ + |(0 << 2) /* CFIS=0 */ + |(0 << 6) /* Endian=0 */ + |(0 << 7); /* Code16=0 */ + zr36060_write_8(zr, 0x002, reg); + + switch (mode) { + + case BUZ_MODE_MOTION_DECOMPRESS: + case BUZ_MODE_STILL_DECOMPRESS: + reg = 0x00; /* Codec mode = decompression */ + break; + + case BUZ_MODE_MOTION_COMPRESS: + case BUZ_MODE_STILL_COMPRESS: + default: + reg = 0xa4; /* Codec mode = compression with variable scale factor */ + break; + + } + zr36060_write_8(zr, 0x003, reg); + + reg = 0x00; /* reserved, mbz */ + zr36060_write_8(zr, 0x004, reg); + + reg = 0xff; /* 510 bits/block */ + zr36060_write_8(zr, 0x005, reg); + + /* JPEG markers */ + reg = (zr->params.jpeg_markers) & 0x38; /* DRI, DQT, DHT */ + if (zr->params.COM_len) + reg |= JPEG_MARKER_COM; + if (zr->params.APP_len) + reg |= JPEG_MARKER_APP; + zr36060_write_8(zr, 0x006, reg); + + reg = (0 << 3) /* DATERR=0 */ + |(0 << 2) /* END=0 */ + |(0 << 1) /* EOI=0 */ + |(0 << 0); /* EOAV=0 */ + zr36060_write_8(zr, 0x007, reg); + + /* code volume */ + + /* Target field size in pixels: */ + tvn = &tvnorms[zr->params.norm]; + size = (tvn->Ha / 2) * (tvn->Wa) / (zr->params.HorDcm) / (zr->params.VerDcm); + + /* Target compressed field size in bits: */ + size = size * 16; /* uncompressed size in bits */ + size = size * zr->params.quality / 400; /* quality = 100 is a compression ratio 1:4 */ + + /* Lower limit (arbitrary, 1 KB) */ + if (size < 8192) + size = 8192; + + /* Upper limit: 7/8 of the code buffers */ + if (size * zr->params.field_per_buff > zr->jpg_bufsize * 7) + size = zr->jpg_bufsize * 7 / zr->params.field_per_buff; + + reg = size; + zr36060_write_32(zr, 0x009, reg); + + /* how do we set initial SF as a function of quality parameter? */ + reg = 0x0100; /* SF=1.0 */ + zr36060_write_16(zr, 0x011, reg); + + reg = 0x00ffffff; /* AF=max */ + zr36060_write_24(zr, 0x013, reg); + + reg = 0x0000; /* test */ + zr36060_write_16(zr, 0x024, reg); +} + +static void zr36060_set_video(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + + reg = (0 << 7) /* Video8=0 */ + |(0 << 6) /* Range=0 */ + |(0 << 3) /* FlDet=0 */ + |(1 << 2) /* FlVedge=1 */ + |(0 << 1) /* FlExt=0 */ + |(0 << 0); /* SyncMstr=0 */ + + /* According to ZR36067 documentation, FlDet should correspond + to the odd_even flag of the ZR36067 */ + if (zr->params.odd_even) + reg |= (1 << 3); + + if (mode != BUZ_MODE_STILL_DECOMPRESS) { + /* limit pixels to range 16..235 as per CCIR-601 */ + reg |= (1 << 6); /* Range=1 */ + } + zr36060_write_8(zr, 0x030, reg); + + reg = (0 << 7) /* VCLKPol=0 */ + |(0 << 6) /* PValPol=0 */ + |(1 << 5) /* PoePol=1 */ + |(0 << 4) /* SImgPol=0 */ + |(0 << 3) /* BLPol=0 */ + |(0 << 2) /* FlPol=0 */ + |(0 << 1) /* HSPol=0, sync on falling edge */ + |(1 << 0); /* VSPol=1 */ + zr36060_write_8(zr, 0x031, reg); + + switch (zr->params.HorDcm) { + default: + case 1: + reg = (0 << 0); + break; /* HScale = 0 */ + + case 2: + reg = (1 << 0); + break; /* HScale = 1 */ + + case 4: + reg = (2 << 0); + break; /* HScale = 2 */ + } + if (zr->params.VerDcm == 2) + reg |= (1 << 2); + zr36060_write_8(zr, 0x032, reg); + + reg = 0x80; /* BackY */ + zr36060_write_8(zr, 0x033, reg); + + reg = 0xe0; /* BackU */ + zr36060_write_8(zr, 0x034, reg); + + reg = 0xe0; /* BackV */ + zr36060_write_8(zr, 0x035, reg); + + /* sync generator */ + + tvn = &tvnorms[zr->params.norm]; + + reg = tvn->Ht - 1; /* Vtotal */ + zr36060_write_16(zr, 0x036, reg); + + reg = tvn->Wt - 1; /* Htotal */ + zr36060_write_16(zr, 0x038, reg); + + reg = 6 - 1; /* VsyncSize */ + zr36060_write_8(zr, 0x03a, reg); + + reg = 100 - 1; /* HsyncSize */ + zr36060_write_8(zr, 0x03b, reg); + + reg = tvn->VStart - 1; /* BVstart */ + zr36060_write_8(zr, 0x03c, reg); + + reg += tvn->Ha / 2; /* BVend */ + zr36060_write_16(zr, 0x03e, reg); + + reg = tvn->HStart - 1; /* BHstart */ + zr36060_write_8(zr, 0x03d, reg); + + reg += tvn->Wa; /* BHend */ + zr36060_write_16(zr, 0x040, reg); + + /* active area */ + reg = zr->params.img_y + tvn->VStart; /* Vstart */ + zr36060_write_16(zr, 0x042, reg); + + reg += zr->params.img_height; /* Vend */ + zr36060_write_16(zr, 0x044, reg); + + reg = zr->params.img_x + tvn->HStart; /* Hstart */ + zr36060_write_16(zr, 0x046, reg); + + reg += zr->params.img_width; /* Hend */ + zr36060_write_16(zr, 0x048, reg); + + /* subimage area */ + reg = zr->params.img_y + tvn->VStart; /* SVstart */ + zr36060_write_16(zr, 0x04a, reg); + + reg += zr->params.img_height; /* SVend */ + zr36060_write_16(zr, 0x04c, reg); + + reg = zr->params.img_x + tvn->HStart; /* SHstart */ + zr36060_write_16(zr, 0x04e, reg); + + reg += zr->params.img_width; /* SHend */ + zr36060_write_16(zr, 0x050, reg); +} + +static void zr36060_set_jpg_SOF(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffc0; /* SOF marker */ + zr36060_write_16(zr, 0x060, reg); + + reg = 17; /* SOF length */ + zr36060_write_16(zr, 0x062, reg); + + reg = 8; /* precision 8 bits */ + zr36060_write_8(zr, 0x064, reg); + + reg = zr->params.img_height / zr->params.VerDcm; /* image height */ + zr36060_write_16(zr, 0x065, reg); + + reg = zr->params.img_width / zr->params.HorDcm; /* image width */ + zr36060_write_16(zr, 0x067, reg); + + reg = 3; /* 3 color components */ + zr36060_write_8(zr, 0x069, reg); + + reg = 0x002100; /* Y component */ + zr36060_write_24(zr, 0x06a, reg); + + reg = 0x011101; /* U component */ + zr36060_write_24(zr, 0x06d, reg); + + reg = 0x021101; /* V component */ + zr36060_write_24(zr, 0x070, reg); +} + +static void zr36060_set_jpg_SOS(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffda; /* SOS marker */ + zr36060_write_16(zr, 0x07a, reg); + + reg = 12; /* SOS length */ + zr36060_write_16(zr, 0x07c, reg); + + reg = 3; /* 3 color components */ + zr36060_write_8(zr, 0x07e, reg); + + reg = 0x0000; /* Y component */ + zr36060_write_16(zr, 0x07f, reg); + + reg = 0x0111; /* U component */ + zr36060_write_16(zr, 0x081, reg); + + reg = 0x0211; /* V component */ + zr36060_write_16(zr, 0x083, reg); + + reg = 0x003f00; /* Start, end spectral scans */ + zr36060_write_24(zr, 0x085, reg); +} + +static void zr36060_set_jpg_DRI(struct zoran *zr) +{ + u32 reg; + + + reg = 0xffdd; /* DRI marker */ + zr36060_write_16(zr, 0x0c0, reg); + + reg = 4; /* DRI length */ + zr36060_write_16(zr, 0x0c2, reg); + + reg = 8; /* length in MCUs */ + zr36060_write_16(zr, 0x0c4, reg); +} + +static void zr36060_set_jpg_DQT(struct zoran *zr) +{ + unsigned i; + unsigned adr; + static const u8 dqt[] = + { + 0xff, 0xdb, /* DHT marker */ + 0x00, 0x84, /* DHT length */ + 0x00, /* table ID 0 */ + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + 0x01, /* table ID 1 */ + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 + }; + + /* write fixed quantitization tables */ + adr = 0x0cc; + for (i = 0; i < sizeof(dqt); ++i) { + zr36060_write_8(zr, adr++, dqt[i]); + } +} + +static void zr36060_set_jpg_DHT(struct zoran *zr) +{ + unsigned i; + unsigned adr; + static const u8 dht[] = + { + 0xff, 0xc4, /* DHT marker */ + 0x01, 0xa2, /* DHT length */ + 0x00, /* table class 0, ID 0 */ + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 8..16 */ + 0x00, /* values for codes of length 2 */ + 0x01, 0x02, 0x03, 0x04, 0x05, /* values for codes of length 3 */ + 0x06, /* values for codes of length 4 */ + 0x07, /* values for codes of length 5 */ + 0x08, /* values for codes of length 6 */ + 0x09, /* values for codes of length 7 */ + 0x0a, /* values for codes of length 8 */ + 0x0b, /* values for codes of length 9 */ + 0x01, /* table class 0, ID 1 */ + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */ + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 9..16 */ + 0x00, 0x01, 0x02, /* values for codes of length 2 */ + 0x03, /* values for codes of length 3 */ + 0x04, /* values for codes of length 4 */ + 0x05, /* values for codes of length 5 */ + 0x06, /* values for codes of length 6 */ + 0x07, /* values for codes of length 7 */ + 0x08, /* values for codes of length 8 */ + 0x09, /* values for codes of length 9 */ + 0x0a, /* values for codes of length 10 */ + 0x0b, /* values for codes of length 11 */ + 0x10, + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, + 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, + 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, + 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, + 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, + 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, + 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, + 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa + }; + + /* write fixed Huffman tables */ + adr = 0x1d4; + for (i = 0; i < sizeof(dht); ++i) { + zr36060_write_8(zr, adr++, dht[i]); + } +} + +static void zr36060_set_jpg_APP(struct zoran *zr) +{ + unsigned adr; + int len, i; + u32 reg; + + + len = zr->params.APP_len; + if (len < 0) + len = 0; + if (len > 60) + len = 60; + + i = zr->params.APPn; + if (i < 0) + i = 0; + if (i > 15) + i = 15; + + reg = 0xffe0 + i; /* APPn marker */ + zr36060_write_16(zr, 0x380, reg); + + reg = len + 2; /* APPn len */ + zr36060_write_16(zr, 0x382, reg); + + /* write APPn data */ + adr = 0x384; + for (i = 0; i < 60; i++) { + zr36060_write_8(zr, adr++, (i < len ? zr->params.APP_data[i] : 0)); + } +} + +static void zr36060_set_jpg_COM(struct zoran *zr) +{ + unsigned adr; + int len, i; + u32 reg; + + + len = zr->params.COM_len; + if (len < 0) + len = 0; + if (len > 60) + len = 60; + + reg = 0xfffe; /* COM marker */ + zr36060_write_16(zr, 0x3c0, reg); + + reg = len + 2; /* COM len */ + zr36060_write_16(zr, 0x3c2, reg); + + /* write COM data */ + adr = 0x3c4; + for (i = 0; i < 60; i++) { + zr36060_write_8(zr, adr++, (i < len ? zr->params.COM_data[i] : 0)); + } +} + +static void zr36060_set_cap(struct zoran *zr, enum zoran_codec_mode mode) +{ + unsigned i; + u32 reg; + + zr36060_reset(zr); + mdelay(10); + + reg = (0 << 7) /* Load=0 */ + |(1 << 0); /* SynRst=1 */ + zr36060_write_8(zr, 0x000, reg); + + zr36060_set_jpg(zr, mode); + zr36060_set_video(zr, mode); + zr36060_set_jpg_SOF(zr); + zr36060_set_jpg_SOS(zr); + zr36060_set_jpg_DRI(zr); + zr36060_set_jpg_DQT(zr); + zr36060_set_jpg_DHT(zr); + zr36060_set_jpg_APP(zr); + zr36060_set_jpg_COM(zr); + + reg = (1 << 7) /* Load=1 */ + |(0 << 0); /* SynRst=0 */ + zr36060_write_8(zr, 0x000, reg); + + /* wait for codec to unbusy */ + for (i = 0; i < 1000; ++i) { + reg = zr36060_read_8(zr, 0x001); + if ((reg & (1 << 7)) == 0) { + DEBUG(printk(KERN_DEBUG "060: loaded, loops=%u\n", i)); + return; + } + udelay(1000); + } + printk(KERN_INFO "060: stuck busy, statux=%02x\n", reg); +} + +static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + int i; + + tvn = &tvnorms[zr->params.norm]; + + /* assert P_Reset */ + btwrite(0, ZR36057_JPC); + + /* re-initialize DMA ring stuff */ + zr->jpg_que_head = 0; + zr->jpg_dma_head = 0; + zr->jpg_dma_tail = 0; + zr->jpg_que_tail = 0; + zr->jpg_seq_num = 0; + for (i = 0; i < BUZ_NUM_STAT_COM; ++i) { + zr->stat_com[i] = 1; /* mark as unavailable to zr36057 */ + } + for (i = 0; i < zr->jpg_nbufs; i++) { + zr->jpg_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */ + } + + /* MJPEG compression mode */ + switch (mode) { + + case BUZ_MODE_MOTION_COMPRESS: + default: + reg = ZR36057_JMC_MJPGCmpMode; + break; + + case BUZ_MODE_MOTION_DECOMPRESS: + reg = ZR36057_JMC_MJPGExpMode; + reg |= ZR36057_JMC_SyncMstr; + /* RJ: The following is experimental - improves the output to screen */ + if (zr->params.VFIFO_FB) + reg |= ZR36057_JMC_VFIFO_FB; + break; + + case BUZ_MODE_STILL_COMPRESS: + reg = ZR36057_JMC_JPGCmpMode; + break; + + case BUZ_MODE_STILL_DECOMPRESS: + reg = ZR36057_JMC_JPGExpMode; + break; + + } + reg |= ZR36057_JMC_JPG; + if (zr->params.field_per_buff == 1) + reg |= ZR36057_JMC_Fld_per_buff; + btwrite(reg, ZR36057_JMC); + + /* vertical */ + btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR); + reg = (6 << ZR36057_VSP_VsyncSize) | (tvn->Ht << ZR36057_VSP_FrmTot); + btwrite(reg, ZR36057_VSP); + reg = ((zr->params.img_y + tvn->VStart) << ZR36057_FVAP_NAY) + | (zr->params.img_height << ZR36057_FVAP_PAY); + btwrite(reg, ZR36057_FVAP); + + /* horizontal */ + btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); + reg = ((tvn->Wt - 100) << ZR36057_HSP_HsyncStart) | (tvn->Wt << ZR36057_HSP_LineTot); + btwrite(reg, ZR36057_HSP); + reg = ((zr->params.img_x + tvn->HStart) << ZR36057_FHAP_NAX) + | (zr->params.img_width << ZR36057_FHAP_PAX); + btwrite(reg, ZR36057_FHAP); + + /* field process parameters */ + if (zr->params.odd_even) + reg = ZR36057_FPP_Odd_Even; + else + reg = 0; + btwrite(reg, ZR36057_FPP); + + /* Set proper VCLK Polarity, else colors will be wrong during playback */ + btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR); + + /* code base address and FIFO threshold */ + reg = virt_to_bus(zr->stat_com); + btwrite(reg, ZR36057_JCBA); + reg = 0x50; + btwrite(reg, ZR36057_JCFT); + + /* JPEG codec guest ID */ + reg = (1 << ZR36057_JCGI_JPEGuestID) | (0 << ZR36057_JCGI_JPEGuestReg); + btwrite(reg, ZR36057_JCGI); + + /* Code transfer guest ID */ + reg = (0 << ZR36057_MCTCR_CodGuestID) | (3 << ZR36057_MCTCR_CodGuestReg); + reg |= ZR36057_MCTCR_CFlush; + btwrite(reg, ZR36057_MCTCR); + + /* deassert P_Reset */ + btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); +} + +static void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) +{ + static int zero = 0; + static int one = 1; + + switch (mode) { + + case BUZ_MODE_MOTION_COMPRESS: + zr36060_set_cap(zr, mode); + zr36057_set_jpg(zr, mode); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &one); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &zero); + + /* deassert P_Reset, assert Code transfer enable */ + btwrite(IRQ_MASK, ZR36057_ISR); + btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + break; + + case BUZ_MODE_MOTION_DECOMPRESS: + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &zero); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &one); + zr36060_set_cap(zr, mode); + zr36057_set_jpg(zr, mode); + + /* deassert P_Reset, assert Code transfer enable */ + btwrite(IRQ_MASK, ZR36057_ISR); + btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + break; + + case BUZ_MODE_IDLE: + default: + /* shut down processing */ + btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); + btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); + btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC); + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); + btwrite(0, ZR36057_ISR); + zr36060_reset(zr); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &one); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &zero); + break; + + } + zr->codec_mode = mode; +} + +/* + * Queue a MJPEG buffer for capture/playback + */ + +static int jpg_qbuf(struct zoran *zr, int frame, enum zoran_codec_mode mode) +{ + unsigned long flags; + int res; + + /* Check if buffers are allocated */ + + if (!zr->jpg_buffers_allocated) { + printk(KERN_ERR "%s: jpg_qbuf: buffers not yet allocated\n", zr->name); + return -ENOMEM; + } + /* Does the user want to stop streaming? */ + + if (frame < 0) { + if (zr->codec_mode == mode) { + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + return 0; + } else { + printk(KERN_ERR "%s: jpg_qbuf - stop streaming but not in streaming mode\n", zr->name); + return -EINVAL; + } + } + /* No grabbing outside the buffer range! */ + + if (frame >= zr->jpg_nbufs) { + printk(KERN_ERR "%s: jpg_qbuf: buffer %d out of range\n", zr->name, frame); + return -EINVAL; + } + /* what is the codec mode right now? */ + + if (zr->codec_mode == BUZ_MODE_IDLE) { + /* Ok load up the zr36060 and go */ + zr36057_enable_jpg(zr, mode); + } else if (zr->codec_mode != mode) { + /* wrong codec mode active - invalid */ + printk(KERN_ERR "%s: jpg_qbuf - codec in wrong mode\n", zr->name); + return -EINVAL; + } + spin_lock_irqsave(&zr->lock, flags); + + /* make sure a grab isn't going on currently with this buffer */ + + switch (zr->jpg_gbuf[frame].state) { + + default: + case BUZ_STATE_DMA: + case BUZ_STATE_PEND: + case BUZ_STATE_DONE: + res = -EBUSY; /* what are you doing? */ + break; + + case BUZ_STATE_USER: + /* since there is at least one unused buffer there's room for at least one more pend[] entry */ + zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = frame; + zr->jpg_gbuf[frame].state = BUZ_STATE_PEND; + zoran_feed_stat_com(zr); + res = 0; + break; + + } + + spin_unlock_irqrestore(&zr->lock, flags); + + /* Start the zr36060 when the first frame is queued */ + if (zr->jpg_que_head == 1) { + btor(ZR36057_JMC_Go_en, ZR36057_JMC); + btwrite(ZR36057_JPC_P_Reset | ZR36057_JPC_CodTrnsEn | ZR36057_JPC_Active, ZR36057_JPC); + } + return res; +} + +/* + * Sync on a MJPEG buffer + */ + +static int jpg_sync(struct zoran *zr, struct zoran_sync *bs) +{ + unsigned long flags; + int frame; + + if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && + zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { + return -EINVAL; + } + while (zr->jpg_que_tail == zr->jpg_dma_tail) { + interruptible_sleep_on(&zr->jpg_capq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + spin_lock_irqsave(&zr->lock, flags); + + frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME]; + + /* buffer should now be in BUZ_STATE_DONE */ + + if (zr->jpg_gbuf[frame].state != BUZ_STATE_DONE) + printk(KERN_ERR "%s: jpg_sync - internal error\n", zr->name); + + *bs = zr->jpg_gbuf[frame].bs; + zr->jpg_gbuf[frame].state = BUZ_STATE_USER; + + spin_unlock_irqrestore(&zr->lock, flags); + + return 0; +} + +/* when this is called the spinlock must be held */ +static void zoran_feed_stat_com(struct zoran *zr) +{ + /* move frames from pending queue to DMA */ + + int frame, i, max_stat_com; + + max_stat_com = (zr->params.TmpDcm == 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); + + while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com + && zr->jpg_dma_head != zr->jpg_que_head) { + + frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME]; + if (zr->params.TmpDcm == 1) { + /* fill 1 stat_com entry */ + i = zr->jpg_dma_head & BUZ_MASK_STAT_COM; + zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus; + } else { + /* fill 2 stat_com entries */ + i = (zr->jpg_dma_head & 1) * 2; + zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus; + zr->stat_com[i + 1] = zr->jpg_gbuf[frame].frag_tab_bus; + } + zr->jpg_gbuf[frame].state = BUZ_STATE_DMA; + zr->jpg_dma_head++; + + } +} + +/* when this is called the spinlock must be held */ +static void zoran_reap_stat_com(struct zoran *zr) +{ + /* move frames from DMA queue to done queue */ + + int i; + u32 stat_com; + unsigned int seq; + unsigned int dif; + int frame; + struct zoran_gbuffer *gbuf; + + /* In motion decompress we don't have a hardware frame counter, + we just count the interrupts here */ + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) + zr->jpg_seq_num++; + + while (zr->jpg_dma_tail != zr->jpg_dma_head) { + if (zr->params.TmpDcm == 1) + i = zr->jpg_dma_tail & BUZ_MASK_STAT_COM; + else + i = (zr->jpg_dma_tail & 1) * 2 + 1; + + stat_com = zr->stat_com[i]; + + if ((stat_com & 1) == 0) { + return; + } + frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; + gbuf = &zr->jpg_gbuf[frame]; + get_fast_time(&gbuf->bs.timestamp); + + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + gbuf->bs.length = (stat_com & 0x7fffff) >> 1; + + /* update sequence number with the help of the counter in stat_com */ + + seq = stat_com >> 24; + dif = (seq - zr->jpg_seq_num) & 0xff; + zr->jpg_seq_num += dif; + } else { + gbuf->bs.length = 0; + } + gbuf->bs.seq = zr->params.TmpDcm == 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; + gbuf->state = BUZ_STATE_DONE; + + zr->jpg_dma_tail++; + } +} + +static void zoran_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 stat, astat; + int count; + struct zoran *zr; + unsigned long flags; + + zr = (struct zoran *) dev_id; + count = 0; + + spin_lock_irqsave(&zr->lock, flags); + while (1) { + /* get/clear interrupt status bits */ + stat = btread(ZR36057_ISR); + astat = stat & IRQ_MASK; + if (!astat) { + break; + } + btwrite(astat, ZR36057_ISR); + IDEBUG(printk(BUZ_DEBUG "-%u: astat %08x stat %08x\n", zr->id, astat, stat)); + +#if (IRQ_MASK & ZR36057_ISR_GIRQ0) + if (astat & ZR36057_ISR_GIRQ0) { + + /* Interrupts may still happen when zr->v4l_memgrab_active is switched off. + We simply ignore them */ + + if (zr->v4l_memgrab_active) { + +/* A lot more checks should be here ... */ + if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0) + printk(KERN_WARNING "%s: BuzIRQ with SnapShot off ???\n", zr->name); + + if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) { + /* There is a grab on a frame going on, check if it has finished */ + + if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) { + /* it is finished, notify the user */ + + zr->v4l_gbuf[zr->v4l_grab_frame].state = BUZ_STATE_DONE; + zr->v4l_grab_frame = NO_GRAB_ACTIVE; + zr->v4l_grab_seq++; + zr->v4l_pend_tail++; + } + } + if (zr->v4l_grab_frame == NO_GRAB_ACTIVE) + wake_up_interruptible(&zr->v4l_capq); + + /* Check if there is another grab queued */ + + if (zr->v4l_grab_frame == NO_GRAB_ACTIVE && + zr->v4l_pend_tail != zr->v4l_pend_head) { + + int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME]; + u32 reg; + + zr->v4l_grab_frame = frame; + + /* Set zr36057 video front end and enable video */ + + /* Buffer address */ + + reg = zr->v4l_gbuf[frame].fbuffer_bus; + btwrite(reg, ZR36057_VDTR); + if (zr->video_interlace) + reg += zr->gbpl; + btwrite(reg, ZR36057_VDBR); + + /* video stride, status, and frame grab register */ + +#ifdef XAWTV_HACK + reg = (zr->gwidth > 720) ? ((zr->gwidth & ~3) - 720) * zr->gbpl / zr->gwidth : 0; +#else + reg = 0; +#endif + if (zr->video_interlace) + reg += zr->gbpl; + reg = (reg << ZR36057_VSSFGR_DispStride); + reg |= ZR36057_VSSFGR_VidOvf; + reg |= ZR36057_VSSFGR_SnapShot; + reg |= ZR36057_VSSFGR_FrameGrab; + btwrite(reg, ZR36057_VSSFGR); + + btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); + } + } + } +#endif /* (IRQ_MASK & ZR36057_ISR_GIRQ0) */ + +#if (IRQ_MASK & ZR36057_ISR_GIRQ1) + if (astat & ZR36057_ISR_GIRQ1) { + unsigned csr = zr36060_read_8(zr, 0x001); + unsigned isr = zr36060_read_8(zr, 0x008); + + IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_GIRQ1 60_code=%02x 60_intr=%02x\n", + zr->name, csr, isr)); + + btand(~ZR36057_ICR_GIRQ1, ZR36057_ICR); + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + } +#endif /* (IRQ_MASK & ZR36057_ISR_GIRQ1) */ + +#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ) + if (astat & ZR36057_ISR_CodRepIRQ) { + IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", zr->name)); + btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR); + } +#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */ + +#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) + if ((astat & ZR36057_ISR_JPEGRepIRQ) && + (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || + zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) { + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + } +#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */ + + count++; + if (count > 10) { + printk(KERN_WARNING "%s: irq loop %d\n", zr->name, count); + if (count > 20) { + btwrite(0, ZR36057_ICR); + printk(KERN_ERR "%s: IRQ lockup, cleared int mask\n", zr->name); + break; + } + } + } + spin_unlock_irqrestore(&zr->lock, flags); +} + +/* Check a zoran_params struct for correctness, insert default params */ + +static int zoran_check_params(struct zoran *zr, struct zoran_params *params) +{ + int err = 0, err0 = 0; + + /* insert constant params */ + + params->major_version = MAJOR_VERSION; + params->minor_version = MINOR_VERSION; + + /* Check input and norm */ + + if (params->input != 0 && params->input != 1) { + err++; + } + if (params->norm != VIDEO_MODE_PAL && params->norm != VIDEO_MODE_NTSC) { + err++; + } + /* Check decimation, set default values for decimation = 1, 2, 4 */ + + switch (params->decimation) { + case 1: + + params->HorDcm = 1; + params->VerDcm = 1; + params->TmpDcm = 1; + params->field_per_buff = 2; + + params->img_x = 0; + params->img_y = 0; + params->img_width = 720; + params->img_height = tvnorms[params->norm].Ha / 2; + break; + + case 2: + + params->HorDcm = 2; + params->VerDcm = 1; + params->TmpDcm = 2; + params->field_per_buff = 1; + + params->img_x = 8; + params->img_y = 0; + params->img_width = 704; + params->img_height = tvnorms[params->norm].Ha / 2; + break; + + case 4: + + params->HorDcm = 4; + params->VerDcm = 2; + params->TmpDcm = 2; + params->field_per_buff = 1; + + params->img_x = 8; + params->img_y = 0; + params->img_width = 704; + params->img_height = tvnorms[params->norm].Ha / 2; + break; + + case 0: + + /* We have to check the data the user has set */ + + if (params->HorDcm != 1 && params->HorDcm != 2 && params->HorDcm != 4) + err0++; + if (params->VerDcm != 1 && params->VerDcm != 2) + err0++; + if (params->TmpDcm != 1 && params->TmpDcm != 2) + err0++; + if (params->field_per_buff != 1 && params->field_per_buff != 2) + err0++; + + if (params->img_x < 0) + err0++; + if (params->img_y < 0) + err0++; + if (params->img_width < 0) + err0++; + if (params->img_height < 0) + err0++; + if (params->img_x + params->img_width > 720) + err0++; + if (params->img_y + params->img_height > tvnorms[params->norm].Ha / 2) + err0++; + if (params->img_width % (16 * params->HorDcm) != 0) + err0++; + if (params->img_height % (8 * params->VerDcm) != 0) + err0++; + + if (err0) { + err++; + } + break; + + default: + err++; + break; + } + + if (params->quality > 100) + params->quality = 100; + if (params->quality < 5) + params->quality = 5; + + if (params->APPn < 0) + params->APPn = 0; + if (params->APPn > 15) + params->APPn = 15; + if (params->APP_len < 0) + params->APP_len = 0; + if (params->APP_len > 60) + params->APP_len = 60; + if (params->COM_len < 0) + params->COM_len = 0; + if (params->COM_len > 60) + params->COM_len = 60; + + if (err) + return -EINVAL; + + return 0; + +} +static void zoran_open_init_params(struct zoran *zr) +{ + int i; + + /* Per default, map the V4L Buffers */ + + zr->map_mjpeg_buffers = 0; + + /* User must explicitly set a window */ + + zr->window_set = 0; + + zr->window.x = 0; + zr->window.y = 0; + zr->window.width = 0; + zr->window.height = 0; + zr->window.chromakey = 0; + zr->window.flags = 0; + zr->window.clips = NULL; + zr->window.clipcount = 0; + + zr->video_interlace = 0; + + zr->v4l_memgrab_active = 0; + zr->v4l_overlay_active = 0; + + zr->v4l_grab_frame = NO_GRAB_ACTIVE; + zr->v4l_grab_seq = 0; + + zr->gwidth = 0; + zr->gheight = 0; + zr->gformat = 0; + zr->gbpl = 0; + + /* DMA ring stuff for V4L */ + + zr->v4l_pend_tail = 0; + zr->v4l_pend_head = 0; + for (i = 0; i < v4l_nbufs; i++) { + zr->v4l_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */ + } + + /* Set necessary params and call zoran_check_params to set the defaults */ + + zr->params.decimation = 1; + + zr->params.quality = 50; /* default compression factor 8 */ + zr->params.odd_even = 1; + + zr->params.APPn = 0; + zr->params.APP_len = 0; /* No APPn marker */ + for (i = 0; i < 60; i++) + zr->params.APP_data[i] = 0; + + zr->params.COM_len = 0; /* No COM marker */ + for (i = 0; i < 60; i++) + zr->params.COM_data[i] = 0; + + zr->params.VFIFO_FB = 0; + + memset(zr->params.reserved, 0, sizeof(zr->params.reserved)); + + zr->params.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT; + + i = zoran_check_params(zr, &zr->params); + if (i) + printk(KERN_ERR "%s: zoran_open_init_params internal error\n", zr->name); +} + +/* + * Open a buz card. Right now the flags stuff is just playing + */ + +static int zoran_open(struct video_device *dev, int flags) +{ + struct zoran *zr = (struct zoran *) dev; + + DEBUG(printk(KERN_INFO ": zoran_open\n")); + + switch (flags) { + + case 0: + if (zr->user) + return -EBUSY; + zr->user++; + + if (v4l_fbuffer_alloc(zr) < 0) { + zr->user--; + return -ENOMEM; + } + /* default setup */ + + zoran_open_init_params(zr); + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + + btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts + + btor(ZR36057_ICR_IntPinEn, ZR36057_ICR); + + break; + + default: + return -EBUSY; + + } + MOD_INC_USE_COUNT; + return 0; +} + +static void zoran_close(struct video_device *dev) +{ + struct zoran *zr = (struct zoran *) dev; + + DEBUG(printk(KERN_INFO ": zoran_close\n")); + + /* disable interrupts */ + btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); + + /* wake up sleeping beauties */ + wake_up_interruptible(&zr->v4l_capq); + wake_up_interruptible(&zr->jpg_capq); + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + zr36057_set_memgrab(zr, 0); + if (zr->v4l_overlay_active) + zr36057_overlay(zr, 0); + + zr->user--; + + v4l_fbuffer_free(zr); + jpg_fbuffer_free(zr); + zr->jpg_nbufs = 0; + + MOD_DEC_USE_COUNT; + DEBUG(printk(KERN_INFO ": zoran_close done\n")); +} + + +static long zoran_read(struct video_device *dev, char *buf, unsigned long count, int nonblock) +{ + return -EINVAL; +} + +static long zoran_write(struct video_device *dev, const char *buf, unsigned long count, int nonblock) +{ + return -EINVAL; +} + +/* + * ioctl routine + */ + + +static int zoran_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct zoran *zr = (struct zoran *) dev; + + switch (cmd) { + + case VIDIOCGCAP: + { + struct video_capability b; + IOCTL_DEBUG(printk("buz ioctl VIDIOCGCAP\n")); + strncpy(b.name, zr->video_dev.name, sizeof(b.name)); + b.type = VID_TYPE_CAPTURE | + VID_TYPE_OVERLAY | + VID_TYPE_CLIPPING | + VID_TYPE_FRAMERAM | + VID_TYPE_SCALES; + /* theoretically we could also flag VID_TYPE_SUBCAPTURE + but this is not even implemented in the BTTV driver */ + + b.channels = 2; /* composite, svhs */ + b.audios = 0; + b.maxwidth = BUZ_MAX_WIDTH; + b.maxheight = BUZ_MAX_HEIGHT; + b.minwidth = BUZ_MIN_WIDTH; + b.minheight = BUZ_MIN_HEIGHT; + 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; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCGCHAN for channel %d\n", v.channel)); + switch (v.channel) { + case 0: + strcpy(v.name, "Composite"); + break; + case 1: + strcpy(v.name, "SVHS"); + break; + default: + return -EINVAL; + } + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = zr->params.norm; + if (copy_to_user(arg, &v, sizeof(v))) { + return -EFAULT; + } + return 0; + } + + /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says: + + * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input." + * ^^^^^^^ + * The famos BTTV driver has it implemented with a struct video_channel argument + * and we follow it for compatibility reasons + * + * BTW: this is the only way the user can set the norm! + */ + + case VIDIOCSCHAN: + { + struct video_channel v; + int input; + int on, res; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCSCHAN: channel=%d, norm=%d\n", v.channel, v.norm)); + switch (v.channel) { + case 0: + input = 3; + break; + case 1: + input = 7; + break; + default: + return -EINVAL; + } + + if (v.norm != VIDEO_MODE_PAL + && v.norm != VIDEO_MODE_NTSC) { + return -EINVAL; + } + zr->params.norm = v.norm; + zr->params.input = v.channel; + + /* We switch overlay off and on since a change in the norm + needs different VFE settings */ + + on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); + + if (on) + zr36057_overlay(zr, 1); + + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + + return 0; + } + + case VIDIOCGTUNER: + case VIDIOCSTUNER: + return -EINVAL; + + case VIDIOCGPICT: + { + struct video_picture p = zr->picture; + + IOCTL_DEBUG(printk("buz ioctl VIDIOCGPICT\n")); + p.depth = zr->buffer.depth; + switch (zr->buffer.depth) { + case 15: + p.palette = VIDEO_PALETTE_RGB555; + break; + + case 16: + p.palette = VIDEO_PALETTE_RGB565; + break; + + case 24: + p.palette = VIDEO_PALETTE_RGB24; + break; + + case 32: + p.palette = VIDEO_PALETTE_RGB32; + break; + } + + 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; + } + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &p); + IOCTL_DEBUG(printk("buz ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n", + p.brightness, p.hue, p.colour, p.contrast, p.depth, p.palette)); + /* The depth and palette values have no meaning to us, + should we return -EINVAL if they don't fit ? */ + zr->picture = p; + return 0; + } + + case VIDIOCCAPTURE: + { + int v, res; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCCAPTURE: %d\n", v)); + /* If there is nothing to do, return immediatly */ + + if ((v && zr->v4l_overlay_active) || (!v && !zr->v4l_overlay_active)) + return 0; + + if (v == 0) { + zr->v4l_overlay_active = 0; + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 0); + /* When a grab is running, the video simply won't be switched on any more */ + } else { + if (!zr->buffer_set || !zr->window_set) { + return -EINVAL; + } + zr->v4l_overlay_active = 1; + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 1); + /* When a grab is running, the video will be switched on when grab is finished */ + } + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + return 0; + } + + case VIDIOCGWIN: + { + IOCTL_DEBUG(printk("buz ioctl VIDIOCGWIN\n")); + if (copy_to_user(arg, &zr->window, sizeof(zr->window))) { + return -EFAULT; + } + return 0; + } + + case VIDIOCSWIN: + { + struct video_clip *vcp; + struct video_window vw; + int on, end, res; + + if (copy_from_user(&vw, arg, sizeof(vw))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n", vw.x, vw.y, vw.width, vw.height, vw.clipcount)); + if (!zr->buffer_set) { + return -EINVAL; + } + /* + * The video front end needs 4-byte alinged line sizes, we correct that + * silently here if necessary + */ + + if (zr->buffer.depth == 15 || zr->buffer.depth == 16) { + end = (vw.x + vw.width) & ~1; /* round down */ + vw.x = (vw.x + 1) & ~1; /* round up */ + vw.width = end - vw.x; + } + if (zr->buffer.depth == 24) { + end = (vw.x + vw.width) & ~3; /* round down */ + vw.x = (vw.x + 3) & ~3; /* round up */ + vw.width = end - vw.x; + } +#if 0 + // At least xawtv seems to care about the following - just leave it away + /* + * Also corrected silently (as long as window fits at all): + * video not fitting the screen + */ +#if 0 + if (vw.x < 0 || vw.y < 0 || vw.x + vw.width > zr->buffer.width || + vw.y + vw.height > zr->buffer.height) { + printk(BUZ_ERR ": VIDIOCSWIN: window does not fit frame buffer: %dx%d+%d*%d\n", + vw.width, vw.height, vw.x, vw.y); + return -EINVAL; + } +#else + if (vw.x < 0) + vw.x = 0; + if (vw.y < 0) + vw.y = 0; + if (vw.x + vw.width > zr->buffer.width) + vw.width = zr->buffer.width - vw.x; + if (vw.y + vw.height > zr->buffer.height) + vw.height = zr->buffer.height - vw.y; +#endif +#endif + + /* Check for vaild parameters */ + if (vw.width < BUZ_MIN_WIDTH || vw.height < BUZ_MIN_HEIGHT || + vw.width > BUZ_MAX_WIDTH || vw.height > BUZ_MAX_HEIGHT) { + return -EINVAL; + } +#ifdef XAWTV_HACK + if (vw.width > 720) + vw.width = 720; +#endif + + zr->window.x = vw.x; + zr->window.y = vw.y; + zr->window.width = vw.width; + zr->window.height = vw.height; + zr->window.chromakey = 0; + zr->window.flags = 0; // RJ: Is this intended for interlace on/off ? + + zr->window.clips = NULL; + zr->window.clipcount = vw.clipcount; + + /* + * If an overlay is running, we have to switch it off + * and switch it on again in order to get the new settings in effect. + * + * We also want to avoid that the overlay mask is written + * when an overlay is running. + */ + + on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + /* + * Write the overlay mask if clips are wanted. + */ + if (vw.clipcount) { + vcp = vmalloc(sizeof(struct video_clip) * (vw.clipcount + 4)); + if (vcp == NULL) { + return -ENOMEM; + } + if (copy_from_user(vcp, vw.clips, sizeof(struct video_clip) * vw.clipcount)) { + vfree(vcp); + return -EFAULT; + } + write_overlay_mask(zr, vcp, vw.clipcount); + vfree(vcp); + } + if (on) + zr36057_overlay(zr, 1); + zr->window_set = 1; + + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + if (res) + return res; + + return 0; + } + + case VIDIOCGFBUF: + { + IOCTL_DEBUG(printk("buz ioctl VIDIOCGFBUF\n")); + if (copy_to_user(arg, &zr->buffer, sizeof(zr->buffer))) { + return -EFAULT; + } + return 0; + } + + case VIDIOCSFBUF: + { + struct video_buffer v; + + if (!capable(CAP_SYS_ADMIN) + || !capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + IOCTL_DEBUG(printk("buz ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n", (u32) v.base, v.width, v.height, v.depth, v.bytesperline)); + if (zr->v4l_overlay_active) { + /* Has the user gotten crazy ... ? */ + return -EINVAL; + } + if (v.depth != 15 + && v.depth != 16 + && v.depth != 24 + && v.depth != 32) { + return -EINVAL; + } + if (v.height <= 0 || v.width <= 0 || v.bytesperline <= 0) { + return -EINVAL; + } + if (v.bytesperline & 3) { + return -EINVAL; + } + if (v.base) { + zr->buffer.base = (void *) ((unsigned long) v.base & ~3); + } + zr->buffer.height = v.height; + zr->buffer.width = v.width; + zr->buffer.depth = v.depth; + zr->buffer.bytesperline = v.bytesperline; + + if (zr->buffer.base) + zr->buffer_set = 1; + zr->window_set = 0; /* The user should set new window parameters */ + return 0; + } + + /* RJ: what is VIDIOCKEY intended to do ??? */ + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + + case VIDIOCSYNC: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCSYNC %d\n", v)); + return v4l_sync(zr, v); + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n", + vm.frame, vm.height, vm.width, vm.format)); + return v4l_grab(zr, &vm); + } + + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + IOCTL_DEBUG(printk("buz ioctl VIDIOCGMBUF\n")); + + vm.size = v4l_nbufs * v4l_bufsize; + vm.frames = v4l_nbufs; + for (i = 0; i < v4l_nbufs; i++) { + vm.offsets[i] = i * v4l_bufsize; + } + + /* The next mmap will map the V4L buffers */ + zr->map_mjpeg_buffers = 0; + + if (copy_to_user(arg, &vm, sizeof(vm))) { + return -EFAULT; + } + return 0; + } + + case VIDIOCGUNIT: + { + struct video_unit vu; + + IOCTL_DEBUG(printk("buz ioctl VIDIOCGUNIT\n")); + vu.video = zr->video_dev.minor; + vu.vbi = VIDEO_NO_UNIT; + vu.radio = VIDEO_NO_UNIT; + vu.audio = VIDEO_NO_UNIT; + vu.teletext = VIDEO_NO_UNIT; + if (copy_to_user(arg, &vu, sizeof(vu))) + return -EFAULT; + return 0; + } + + /* + * RJ: In principal we could support subcaptures for V4L grabbing. + * Not even the famous BTTV driver has them, however. + * If there should be a strong demand, one could consider + * to implement them. + */ + case VIDIOCGCAPTURE: + case VIDIOCSCAPTURE: + return -EINVAL; + + case BUZIOC_G_PARAMS: + { + IOCTL_DEBUG(printk("buz ioctl BUZIOC_G_PARAMS\n")); + if (copy_to_user(arg, &(zr->params), sizeof(zr->params))) + return -EFAULT; + return 0; + } + + case BUZIOC_S_PARAMS: + { + struct zoran_params bp; + int input, on; + + if (zr->codec_mode != BUZ_MODE_IDLE) { + return -EINVAL; + } + if (copy_from_user(&bp, arg, sizeof(bp))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_S_PARAMS\n")); + + /* Check the params first before overwriting our internal values */ + + if (zoran_check_params(zr, &bp)) + return -EINVAL; + + zr->params = bp; + + /* Make changes of input and norm go into effect immediatly */ + + /* We switch overlay off and on since a change in the norm + needs different VFE settings */ + + on = zr->v4l_overlay_active && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + input = zr->params.input == 0 ? 3 : 7; + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); + + if (on) + zr36057_overlay(zr, 1); + + if (copy_to_user(arg, &bp, sizeof(bp))) { + return -EFAULT; + } + return 0; + } + + case BUZIOC_REQBUFS: + { + struct zoran_requestbuffers br; + + if (zr->jpg_buffers_allocated) { + return -EINVAL; + } + if (copy_from_user(&br, arg, sizeof(br))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_REQBUFS count = %lu size=%lu\n", + br.count, br.size)); + /* Enforce reasonable lower and upper limits */ + if (br.count < 4) + br.count = 4; /* Could be choosen smaller */ + if (br.count > BUZ_MAX_FRAME) + br.count = BUZ_MAX_FRAME; + br.size = PAGE_ALIGN(br.size); + if (br.size < 8192) + br.size = 8192; /* Arbitrary */ + /* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */ + if (br.size > (512 * 1024)) + br.size = (512 * 1024); /* 512 K should be enough */ + if (zr->need_contiguous && br.size > MAX_KMALLOC_MEM) + br.size = MAX_KMALLOC_MEM; + + zr->jpg_nbufs = br.count; + zr->jpg_bufsize = br.size; + + if (jpg_fbuffer_alloc(zr)) + return -ENOMEM; + + /* The next mmap will map the MJPEG buffers */ + zr->map_mjpeg_buffers = 1; + + if (copy_to_user(arg, &br, sizeof(br))) { + return -EFAULT; + } + return 0; + } + + case BUZIOC_QBUF_CAPT: + { + int nb; + + if (copy_from_user((void *) &nb, (void *) arg, sizeof(int))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_QBUF_CAPT %d\n", nb)); + return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS); + } + + case BUZIOC_QBUF_PLAY: + { + int nb; + + if (copy_from_user((void *) &nb, (void *) arg, sizeof(int))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_QBUF_PLAY %d\n", nb)); + return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_DECOMPRESS); + } + + case BUZIOC_SYNC: + { + struct zoran_sync bs; + int res; + + IOCTL_DEBUG(printk("buz ioctl BUZIOC_SYNC\n")); + res = jpg_sync(zr, &bs); + if (copy_to_user(arg, &bs, sizeof(bs))) { + return -EFAULT; + } + return res; + } + + case BUZIOC_G_STATUS: + { + struct zoran_status bs; + int norm, input, status; + unsigned long timeout; + + if (zr->codec_mode != BUZ_MODE_IDLE) { + return -EINVAL; + } + if (copy_from_user(&bs, arg, sizeof(bs))) { + return -EFAULT; + } + IOCTL_DEBUG(printk("buz ioctl BUZIOC_G_STATUS\n")); + switch (bs.input) { + case 0: + input = 3; + break; + case 1: + input = 7; + break; + default: + return -EINVAL; + } + + /* Set video norm to VIDEO_MODE_AUTO */ + + norm = VIDEO_MODE_AUTO; + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &norm); + + /* sleep 1 second */ + + schedule_timeout(HZ); + + /* Get status of video decoder */ + + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_GET_STATUS, &status); + bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0; + bs.norm = (status & DECODER_STATUS_NTSC) ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL; + bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0; + + /* restore previous input and norm */ + input = zr->params.input == 0 ? 3 : 7; + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); + + if (copy_to_user(arg, &bs, sizeof(bs))) { + return -EFAULT; + } + return 0; + } + + default: + return -ENOIOCTLCMD; + + } + return 0; +} + + +/* + * This maps the buffers to user space. + * + * Depending on the state of zr->map_mjpeg_buffers + * the V4L or the MJPEG buffers are mapped + * + */ + +static int zoran_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct zoran *zr = (struct zoran *) dev; + unsigned long start = (unsigned long) adr; + unsigned long page, pos, todo, fraglen; + int i, j; + + if (zr->map_mjpeg_buffers) { + /* Map the MJPEG buffers */ + + if (!zr->jpg_buffers_allocated) { + return -ENOMEM; + } + if (size > zr->jpg_nbufs * zr->jpg_bufsize) { + return -EINVAL; + } + + for (i = 0; i < zr->jpg_nbufs; i++) { + for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) { + fraglen = (zr->jpg_gbuf[i].frag_tab[2 * j + 1] & ~1) << 1; + todo = size; + if (todo > fraglen) + todo = fraglen; + pos = (unsigned long) zr->jpg_gbuf[i].frag_tab[2 * j]; + page = virt_to_phys(bus_to_virt(pos)); /* should just be pos on i386 */ + if (remap_page_range(start, page, todo, PAGE_SHARED)) { + printk(KERN_ERR "%s: zoran_mmap(V4L): remap_page_range failed\n", zr->name); + return -EAGAIN; + } + size -= todo; + start += todo; + if (size == 0) + break; + if (zr->jpg_gbuf[i].frag_tab[2 * j + 1] & 1) + break; /* was last fragment */ + } + if (size == 0) + break; + } + } else { + /* Map the V4L buffers */ + + if (size > v4l_nbufs * v4l_bufsize) { + return -EINVAL; + } + + for (i = 0; i < v4l_nbufs; i++) { + todo = size; + if (todo > v4l_bufsize) + todo = v4l_bufsize; + page = zr->v4l_gbuf[i].fbuffer_phys; + DEBUG(printk("V4L remap page range %d 0x%x %d to 0x%x\n", i, page, todo, start)); + if (remap_page_range(start, page, todo, PAGE_SHARED)) { + printk(KERN_ERR "%s: zoran_mmap(V4L): remap_page_range failed\n", zr->name); + return -EAGAIN; + } + size -= todo; + start += todo; + if (size == 0) + break; + } + } + return 0; +} + +static int zoran_init_done(struct video_device *dev) +{ + return 0; +} + +static struct video_device zoran_template = +{ + BUZ_NAME, + VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | + VID_TYPE_SCALES | VID_TYPE_SUBCAPTURE, + VID_HARDWARE_BT848, /* Not true, but the buz is not yet in the list */ + zoran_open, + zoran_close, + zoran_read, + zoran_write, + NULL, + zoran_ioctl, + zoran_mmap, + zoran_init_done, + NULL, + 0, + 0 +}; + +static int zr36057_init(int i) +{ + struct zoran *zr = &zoran[i]; + unsigned long mem; + unsigned mem_needed; + int j; + int rev; + + /* reset zr36057 */ + btwrite(0, ZR36057_SPGPPCR); + mdelay(10); + + /* default setup of all parameters which will persist beetween opens */ + + zr->user = 0; + + zr->map_mjpeg_buffers = 0; /* Map V4L buffers by default */ + + zr->jpg_nbufs = 0; + zr->jpg_bufsize = 0; + zr->jpg_buffers_allocated = 0; + + zr->buffer_set = 0; /* Flag if frame buffer has been set */ + zr->buffer.base = (void *) vidmem; + zr->buffer.width = 0; + zr->buffer.height = 0; + zr->buffer.depth = 0; + zr->buffer.bytesperline = 0; + + zr->params.norm = default_norm ? 1 : 0; /* Avoid nonsense settings from user */ + zr->params.input = default_input ? 1 : 0; /* Avoid nonsense settings from user */ + zr->video_interlace = 0; + + /* Should the following be reset at every open ? */ + + zr->picture.colour = 32768; + zr->picture.brightness = 32768; + zr->picture.hue = 32768; + zr->picture.contrast = 32768; + zr->picture.whiteness = 0; + zr->picture.depth = 0; + zr->picture.palette = 0; + + for (j = 0; j < VIDEO_MAX_FRAME; j++) { + zr->v4l_gbuf[i].fbuffer = 0; + zr->v4l_gbuf[i].fbuffer_phys = 0; + zr->v4l_gbuf[i].fbuffer_bus = 0; + } + + zr->stat_com = 0; + + /* default setup (will be repeated at every open) */ + + zoran_open_init_params(zr); + + /* allocate memory *before* doing anything to the hardware in case allocation fails */ + + /* STAT_COM table and overlay mask */ + + mem_needed = (BUZ_NUM_STAT_COM + ((BUZ_MAX_WIDTH + 31) / 32) * BUZ_MAX_HEIGHT) * 4; + mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL); + if (!mem) { + return -ENOMEM; + } + memset((void *) mem, 0, mem_needed); + + zr->stat_com = (u32 *) mem; + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */ + } + zr->overlay_mask = (u32 *) (mem + BUZ_NUM_STAT_COM * 4); + + /* Initialize zr->jpg_gbuf */ + + for (j = 0; j < BUZ_MAX_FRAME; j++) { + zr->jpg_gbuf[j].frag_tab = 0; + zr->jpg_gbuf[j].frag_tab_bus = 0; + zr->jpg_gbuf[j].state = BUZ_STATE_USER; + zr->jpg_gbuf[j].bs.frame = j; + } + + /* take zr36057 out of reset now */ + btwrite(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR); + mdelay(10); + + /* stop all DMA processes */ + btwrite(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + /* assert P_Reset */ + btwrite(0, ZR36057_JPC); + + switch(zr->board) + { + case BOARD_BUZ: + + /* set up GPIO direction */ + btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR); + + /* Set up guest bus timing - Guests 0..3 Tdur=12, Trec=3 */ + btwrite((GPIO_MASK << 24) | 0x8888, ZR36057_GPPGCR1); + mdelay(10); + + /* reset video decoder */ + + GPIO(zr, 0, 0); + mdelay(10); + GPIO(zr, 0, 1); + mdelay(10); + + /* reset JPEG codec */ + zr36060_sleep(zr, 0); + mdelay(10); + zr36060_reset(zr); + mdelay(10); + zr36060_sleep(zr, 1); + mdelay(10); + + /* display codec revision */ + if ((rev=zr36060_read_8(zr, 0x022)) == 0x33) { + printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n", + zr->name, zr36060_read_8(zr, 0x023)); + } else { + printk(KERN_ERR "%s: Zoran ZR36060 not found (Rev=%d)\n", zr->name, rev); + kfree((void *) zr->stat_com); + return -1; + } + break; + + case BOARD_LML33: +// btwrite(btread(ZR36057_SPGPPCR)&~ZR36057_SPGPPCR_SoftReset , ZR36057_SPGPPCR); +// udelay(100); +// btwrite(btread(ZR36057_SPGPPCR)|ZR36057_SPGPPCR_SoftReset , ZR36057_SPGPPCR); +// udelay(1000); + + /* + * Set up the GPIO direction + */ + btwrite(btread(ZR36057_SPGPPCR_SoftReset)|0 , ZR36057_SPGPPCR); + /* Set up guest bus timing - Guests 0..2 Tdur=12, Trec=3 */ + btwrite(0xFF00F888, ZR36057_GPPGCR1); + mdelay(10); + GPIO(zr, 5, 0); /* Analog video bypass */ + udelay(3000); + GPIO(zr, 0, 0); /* Reset 819 */ + udelay(3000); + GPIO(zr, 0, 1); /* 819 back */ + udelay(3000); + /* reset JPEG codec */ + zr36060_sleep(zr, 0); + udelay(3000); + zr36060_reset(zr); + udelay(3000); + zr36060_sleep(zr, 1); + udelay(3000); + + /* display codec revision */ + if ((rev=zr36060_read_8(zr, 0x022)) == 0x33) { + printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n", + zr->name, zr36060_read_8(zr, 0x023)); + } else { + printk(KERN_ERR "%s: Zoran ZR36060 not found (rev=%d)\n", zr->name, rev); +// kfree((void *) zr->stat_com); +// return -1; + } + break; + } + /* i2c */ + memcpy(&zr->i2c, &zoran_i2c_bus_template, sizeof(struct i2c_bus)); + sprintf(zr->i2c.name, "zoran%u%u", zr->id); + zr->i2c.data = zr; + if (i2c_register_bus(&zr->i2c) < 0) { + kfree((void *) zr->stat_com); + return -1; + } + /* + * Now add the template and register the device unit. + */ + memcpy(&zr->video_dev, &zoran_template, sizeof(zoran_template)); + sprintf(zr->video_dev.name, "zoran%u", zr->id); + if (video_register_device(&zr->video_dev, VFL_TYPE_GRABBER) < 0) { + i2c_unregister_bus(&zr->i2c); + kfree((void *) zr->stat_com); + return -1; + } + /* toggle JPEG codec sleep to sync PLL */ + zr36060_sleep(zr, 1); + mdelay(10); + zr36060_sleep(zr, 0); + mdelay(10); + + /* Enable bus-mastering */ + pci_set_master(zr->pci_dev); + + j = zr->params.input == 0 ? 3 : 7; + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &j); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm); + i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm); + + /* set individual interrupt enables (without GIRQ0) + but don't global enable until zoran_open() */ + + btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ0, ZR36057_ICR); + + if(request_irq(zr->pci_dev->irq, zoran_irq, + SA_SHIRQ | SA_INTERRUPT, zr->name, (void *) zr)<0) + { + printk(KERN_ERR "%s: Can't assign irq.\n", zr->name); + video_unregister_device(&zr->video_dev); + i2c_unregister_bus(&zr->i2c); + kfree((void *) zr->stat_com); + return -1; + } + zr->initialized = 1; + return 0; +} + + + +static void release_zoran(void) +{ + u8 command; + int i; + struct zoran *zr; + + for (i = 0; i < zoran_num; i++) { + zr = &zoran[i]; + + if (!zr->initialized) + continue; + + /* unregister i2c_bus */ + i2c_unregister_bus((&zr->i2c)); + + /* disable PCI bus-mastering */ + pci_read_config_byte(zr->pci_dev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte(zr->pci_dev, PCI_COMMAND, command); + + /* put chip into reset */ + btwrite(0, ZR36057_SPGPPCR); + + free_irq(zr->pci_dev->irq, zr); + + /* unmap and free memory */ + + kfree((void *) zr->stat_com); + + iounmap(zr->zr36057_mem); + + video_unregister_device(&zr->video_dev); + } +} + +/* + * Scan for a Buz card (actually for the PCI controller ZR36057), + * request the irq and map the io memory + */ + +static int find_zr36057(void) +{ + unsigned char latency; + struct zoran *zr; + struct pci_dev *dev = NULL; + int result; + + zoran_num = 0; + + while (zoran_num < BUZ_MAX + && (dev = pci_find_device(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, dev)) != NULL) { + zr = &zoran[zoran_num]; + zr->pci_dev = dev; + zr->zr36057_mem = NULL; + zr->id = zoran_num; + sprintf(zr->name, "zoran%u", zr->id); + + spin_lock_init(&zr->lock); + + zr->zr36057_adr = zr->pci_dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; + pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision); + if (zr->revision < 2) { + printk(KERN_INFO "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n", + zr->name, zr->revision, zr->pci_dev->irq, zr->zr36057_adr); + } else { + unsigned short ss_vendor_id, ss_id; + + pci_read_config_word(zr->pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor_id); + pci_read_config_word(zr->pci_dev, PCI_SUBSYSTEM_ID, &ss_id); + printk(KERN_INFO "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n", + zr->name, zr->revision, zr->pci_dev->irq, zr->zr36057_adr); + printk(KERN_INFO "%s: subsystem vendor=0x%04x id=0x%04x\n", + zr->name, ss_vendor_id, ss_id); + if(ss_vendor_id==0xFF10 && ss_id == 0xDE41) + { + zr->board = BOARD_LML33; + printk(KERN_INFO "%s: LML33 detected.\n", zr->name); + } + } + + zr->zr36057_mem = ioremap(zr->zr36057_adr, 0x1000); + + /* set PCI latency timer */ + pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, &latency); + if (latency != 48) { + printk(KERN_INFO "%s: Changing PCI latency from %d to 48.\n", zr->name, latency); + latency = 48; + pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, latency); + } + zoran_num++; + } + if (zoran_num == 0) + printk(KERN_INFO "zoran: no cards found.\n"); + + return zoran_num; +} + +#include "chipsets.h" + +static void handle_chipset(void) +{ + int index; + struct pci_dev *dev = NULL; + + for (index = 0; index < sizeof(black) / sizeof(black[0]); index++) { + if ((dev = pci_find_device(black[index].vendor, black[index].device, dev)) != NULL) { + printk(KERN_INFO ": Host bridge: %s, ", black[index].name); + switch (black[index].action) { + + case TRITON: + printk("enabling Triton support.\n"); + triton = 1; + break; + + case NATOMA: + printk("enabling Natoma workaround.\n"); + natoma = 1; + break; + } + } + } +} + +#ifdef MODULE +int init_module(void) +#else +int init_zoran_cards(struct video_init *unused) +#endif +{ + int i; + + + printk(KERN_INFO "Zoran driver 1.00 (c) 1999 Rainer Johanni, Dave Perks.\n"); + + /* Look for Buz cards */ + + if (find_zr36057() <= 0) { + return -EIO; + } + printk(KERN_INFO"zoran: %d zoran card(s) found\n", zoran_num); + + if (zoran_num == 0) + return -ENXIO; + + + /* check the parameters we have been given, adjust if necessary */ + + if (v4l_nbufs < 0) + v4l_nbufs = 0; + if (v4l_nbufs > VIDEO_MAX_FRAME) + v4l_nbufs = VIDEO_MAX_FRAME; + /* The user specfies the in KB, we want them in byte (and page aligned) */ + v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); + if (v4l_bufsize < 32768) + v4l_bufsize = 32768; + /* 2 MB is arbitrary but sufficient for the maximum possible images */ + if (v4l_bufsize > 2048 * 1024) + v4l_bufsize = 2048 * 1024; + + printk(KERN_INFO "zoran: using %d V4L buffers of size %d KB\n", v4l_nbufs, v4l_bufsize >> 10); + + /* Use parameter for vidmem or try to find a video card */ + + if (vidmem) { + printk(KERN_INFO "zoran: Using supplied video memory base address @ 0x%lx\n", vidmem); + } + + /* check if we have a Triton or Natome chipset */ + + handle_chipset(); + + /* take care of Natoma chipset and a revision 1 zr36057 */ + + for (i = 0; i < zoran_num; i++) { + if (natoma && zoran[i].revision <= 1) { + zoran[i].need_contiguous = 1; + printk(KERN_INFO "%s: ZR36057/Natome bug, max. buffer size is 128K\n", zoran[i].name); + } else { + zoran[i].need_contiguous = 0; + } + } + + /* initialize the Buzs */ + + /* We have to know which ones must be released if an error occurs */ + for (i = 0; i < zoran_num; i++) + zoran[i].initialized = 0; + + for (i = 0; i < zoran_num; i++) { + if (zr36057_init(i) < 0) { + release_zoran(); + return -EIO; + } + } + + return 0; +} + + + +#ifdef MODULE + +void cleanup_module(void) +{ + release_zoran(); +} + +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/buz.h linux/drivers/char/buz.h --- v2.2.11/linux/drivers/char/buz.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/buz.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,319 @@ +/* + buz - Iomega Buz driver + + Copyright (C) 1999 Rainer Johanni + + based on + + buz.0.0.3 Copyright (C) 1998 Dave Perks + + and + + 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. + */ + +#ifndef _BUZ_H_ +#define _BUZ_H_ + +/* The Buz only supports a maximum width of 720, but some V4L + applications (e.g. xawtv are more happy with 768). + If XAWTV_HACK is defined, we try to fake a device with bigger width */ + +#define XAWTV_HACK + +#ifdef XAWTV_HACK +#define BUZ_MAX_WIDTH 768 /* never display more than 768 pixels */ +#else +#define BUZ_MAX_WIDTH 720 /* never display more than 720 pixels */ +#endif +#define BUZ_MAX_HEIGHT 576 /* never display more than 576 rows */ +#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ +#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ + +struct zoran_requestbuffers { + unsigned long count; /* Number of buffers for MJPEG grabbing */ + unsigned long size; /* Size PER BUFFER in bytes */ +}; + +struct zoran_sync { + unsigned long frame; /* number of buffer that has been free'd */ + unsigned long length; /* number of code bytes in buffer (capture only) */ + unsigned long seq; /* frame sequence number */ + struct timeval timestamp; /* timestamp */ +}; + +struct zoran_status { + int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ + int signal; /* Returned: 1 if valid video signal detected */ + int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int color; /* Returned: 1 if color signal detected */ +}; + +struct zoran_params { + + /* The following parameters can only be queried */ + + int major_version; /* Major version number of driver */ + int minor_version; /* Minor version number of driver */ + + /* Main control parameters */ + + int input; /* Input channel: 0 = Composite, 1 = S-VHS */ + int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int decimation; /* decimation of captured video, + enlargement of video played back. + Valid values are 1, 2, 4 or 0. + 0 is a special value where the user + has full control over video scaling */ + + /* The following parameters only have to be set if decimation==0, + for other values of decimation they provide the data how the image is captured */ + + int HorDcm; /* Horizontal decimation: 1, 2 or 4 */ + int VerDcm; /* Vertical decimation: 1 or 2 */ + int TmpDcm; /* Temporal decimation: 1 or 2, + if TmpDcm==2 in capture every second frame is dropped, + in playback every frame is played twice */ + int field_per_buff; /* Number of fields per buffer: 1 or 2 */ + int img_x; /* start of image in x direction */ + int img_y; /* start of image in y direction */ + int img_width; /* image width BEFORE decimation, + must be a multiple of HorDcm*16 */ + int img_height; /* image height BEFORE decimation, + must be a multiple of VerDcm*8 */ + + /* --- End of parameters for decimation==0 only --- */ + + /* JPEG control parameters */ + + int quality; /* Measure for quality of compressed images. + Scales linearly with the size of the compressed images. + Must be beetween 0 and 100, 100 is a compression + ratio of 1:4 */ + + int odd_even; /* Which field should come first ??? */ + + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ + + unsigned long jpeg_markers; /* Which markers should go into the JPEG output. + Unless you exactly know what you do, leave them untouched. + Inluding less markers will make the resulting code + smaller, but there will be fewer aplications + which can read it. + The presence of the APP and COM marker is + influenced by APP0_len and COM_len ONLY! */ +#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ +#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ +#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ +#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */ + + int VFIFO_FB; /* Flag for enabling Video Fifo Feedback. + If this flag is turned on and JPEG decompressing + is going to the screen, the decompress process + is stopped every time the Video Fifo is full. + This enables a smooth decompress to the screen + but the video output signal will get scrambled */ + + /* Misc */ + + char reserved[312]; /* Makes 512 bytes for this structure */ +}; + +/* + Private IOCTL to set up for displaying MJPEG + */ +#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params) +#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params) +#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers) +#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) +#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) +#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync) +#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status) + + +#ifdef __KERNEL__ + +#define BUZ_NUM_STAT_COM 4 +#define BUZ_MASK_STAT_COM 3 + +#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ +#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ + +#if VIDEO_MAX_FRAME <= 32 +#define V4L_MAX_FRAME 32 +#elif VIDEO_MAX_FRAME <= 64 +#define V4L_MAX_FRAME 64 +#else +#error "Too many video frame buffers to handle" +#endif +#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) + + +#include "zr36057.h" + +enum zoran_codec_mode { + BUZ_MODE_IDLE, /* nothing going on */ + BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ + BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ + BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ + BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ +}; + +enum zoran_buffer_state { + BUZ_STATE_USER, /* buffer is owned by application */ + BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */ + BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */ + BUZ_STATE_DONE /* buffer is ready to return to application */ +}; + +struct zoran_gbuffer { + u32 *frag_tab; /* addresses of frag table */ + u32 frag_tab_bus; /* same value cached to save time in ISR */ + enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ + struct zoran_sync bs; /* DONE: info to return to application */ +}; + +struct v4l_gbuffer { + char *fbuffer; /* virtual address of frame buffer */ + unsigned long fbuffer_phys; /* physical address of frame buffer */ + unsigned long fbuffer_bus; /* bus address of frame buffer */ + enum zoran_buffer_state state; /* state: unused/pending/done */ +}; + +struct zoran { + struct video_device video_dev; + struct i2c_bus i2c; + + int initialized; /* flag if zoran has been correctly initalized */ + int user; /* number of current users (0 or 1) */ + + unsigned short id; /* number of this device */ + char name[32]; /* name of this device */ + struct pci_dev *pci_dev; /* PCI device */ + unsigned char revision; /* revision of zr36057 */ + int board; /* Board type */ +#define BOARD_BUZ 0 +#define BOARD_LML33 1 + unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */ + unsigned char *zr36057_mem; /* pointer to mapped IO memory */ + + int map_mjpeg_buffers; /* Flag which bufferset will map by next mmap() */ + + spinlock_t lock; /* Spinlock */ + + /* Video for Linux parameters */ + + struct video_picture picture; /* Current picture params */ + struct video_buffer buffer; /* Current buffer params */ + struct video_window window; /* Current window params */ + int buffer_set, window_set; /* Flags if the above structures are set */ + int video_interlace; /* Image on screen is interlaced */ + + u32 *overlay_mask; + + struct wait_queue *v4l_capq; /* wait here for grab to finish */ + + int v4l_overlay_active; /* Overlay grab is activated */ + int v4l_memgrab_active; /* Memory grab is activated */ + + int v4l_grab_frame; /* Frame number being currently grabbed */ +#define NO_GRAB_ACTIVE (-1) + int v4l_grab_seq; /* Number of frames grabbed */ + int gwidth; /* Width of current memory capture */ + int gheight; /* Height of current memory capture */ + int gformat; /* Format of ... */ + int gbpl; /* byte per line of ... */ + + /* V4L grab queue of frames pending */ + + unsigned v4l_pend_head; + unsigned v4l_pend_tail; + int v4l_pend[V4L_MAX_FRAME]; + + struct v4l_gbuffer v4l_gbuf[VIDEO_MAX_FRAME]; /* V4L buffers' info */ + + /* Buz MJPEG parameters */ + + unsigned long jpg_nbufs; /* Number of buffers */ + unsigned long jpg_bufsize; /* Size of mjpeg buffers in bytes */ + int jpg_buffers_allocated; /* Flag if buffers are allocated */ + int need_contiguous; /* Flag if contiguous buffers are needed */ + + enum zoran_codec_mode codec_mode; /* status of codec */ + struct zoran_params params; /* structure with a lot of things to play with */ + + struct wait_queue *jpg_capq; /* wait here for grab to finish */ + + /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ + /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ + /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ + unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ + unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ + unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ + unsigned long jpg_que_tail; /* Index of last buffer in queue */ + unsigned long jpg_seq_num; /* count of frames since grab/play started */ + + /* zr36057's code buffer table */ + u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ + + /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ + int jpg_pend[BUZ_MAX_FRAME]; + + /* array indexed by frame number */ + struct zoran_gbuffer jpg_gbuf[BUZ_MAX_FRAME]; /* MJPEG buffers' info */ +}; + +#endif + +/*The following should be done in more portable way. It depends on define + of _ALPHA_BUZ in the Makefile. */ + +#ifdef _ALPHA_BUZ +#define btwrite(dat,adr) writel((dat),(char *) (zr->zr36057_adr+(adr))) +#define btread(adr) readl(zr->zr36057_adr+(adr)) +#else +#define btwrite(dat,adr) writel((dat), (char *) (zr->zr36057_mem+(adr))) +#define btread(adr) readl(zr->zr36057_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) + +#define I2C_TSA5522 0xc2 +#define I2C_TDA9850 0xb6 +#define I2C_HAUPEE 0xa0 +#define I2C_STBEE 0xae +#define I2C_SAA7111 0x48 +#define I2C_SAA7185 0x88 + +#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 -u --recursive --new-file v2.2.11/linux/drivers/char/chipsets.h linux/drivers/char/chipsets.h --- v2.2.11/linux/drivers/char/chipsets.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/chipsets.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,41 @@ +static const struct { + unsigned short vendor; + unsigned short device; + enum { + TRITON, + NATOMA + } action; + const char *name; +} black[] = { + + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, TRITON, "82437" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX, TRITON, "82437VX Triton II" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439, TRITON, "82439HX Triton II" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82439TX, TRITON, "82439TX" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, NATOMA, "82441FX Natoma" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_0, NATOMA, "440LX - 82443LX PAC Host" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443LX_1, NATOMA, "440LX - 82443LX PAC AGP" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0, NATOMA, "440BX - 82443BX Host" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_1, NATOMA, "440BX - 82443BX AGP" + }, + { + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, NATOMA, "440BX - 82443BX Host (no AGP)" + }, +}; diff -u --recursive --new-file v2.2.11/linux/drivers/char/dz.c linux/drivers/char/dz.c --- v2.2.11/linux/drivers/char/dz.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/dz.c Wed Aug 25 17:29:47 1999 @@ -902,7 +902,7 @@ dz_out (info, DZ_TCR, tmp); - schedule_timeout(jiffies + duration); + schedule_timeout(duration); tmp &= ~mask; dz_out (info, DZ_TCR, tmp); @@ -1093,7 +1093,7 @@ if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; - schedule_timeout(jiffies + info->close_delay); + schedule_timeout(info->close_delay); } wake_up_interruptible (&info->open_wait); } diff -u --recursive --new-file v2.2.11/linux/drivers/char/generic_serial.c linux/drivers/char/generic_serial.c --- v2.2.11/linux/drivers/char/generic_serial.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/generic_serial.c Wed Aug 25 17:29:47 1999 @@ -94,7 +94,7 @@ #ifndef TWO_THREE /* These are new in 2.3. The source now uses 2.3 syntax, and here is the compatibility define... */ -#define waitq_head_t struct wait_queue * +#define wait_queue_head_t struct wait_queue * #define DECLARE_MUTEX(name) struct semaphore name = MUTEX #define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL } diff -u --recursive --new-file v2.2.11/linux/drivers/char/i2c.c linux/drivers/char/i2c.c --- v2.2.11/linux/drivers/char/i2c.c Thu Jan 14 22:53:02 1999 +++ linux/drivers/char/i2c.c Wed Aug 25 17:29:47 1999 @@ -40,6 +40,14 @@ extern int i2c_tuner_init(void); extern int msp3400c_init(void); #endif +#ifdef CONFIG_VIDEO_BUZ +extern int saa7111_init(void); +extern int saa7185_init(void); +#endif +#ifdef CONFIG_VIDEO_LML33 +extern int bt819_init(void); +extern int bt856_init(void); +#endif int i2c_init(void) { @@ -50,6 +58,14 @@ i2c_tuner_init(); msp3400c_init(); #endif +#ifdef CONFIG_VIDEO_BUZ + saa7111_init(); + saa7185_init(); +#endif +#ifdef CONFIG_VIDEO_LML33 + bt819_init(); + bt856_init(); +#endif return 0; } diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/Makefile linux/drivers/char/ip2/Makefile --- v2.2.11/linux/drivers/char/ip2/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/Makefile Wed Aug 25 17:29:47 1999 @@ -0,0 +1,12 @@ + +all: ip2mkdev ip2trace ip2stat + +ip2mkdev: ip2mkdev.c + cc -o ip2mkdev ip2mkdev.c + +ip2trace: ip2trace.c + cc -o ip2trace ip2trace.c + +ip2stat: ip2stat.c + cc -o ip2stat ip2stat.c + diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/fip_firm.h linux/drivers/char/ip2/fip_firm.h --- v2.2.11/linux/drivers/char/ip2/fip_firm.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/fip_firm.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,2149 @@ +/* fip_firm.h - Intelliport II loadware */ +/* -31232 bytes read from ff.lod */ + +unsigned char fip_firm[] __initdata = { +0x3C,0x42,0x8A,0xE1,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x57,0x65,0x64,0x20,0x4A,0x75,0x6E,0x20,0x32,0x33,0x20,0x31,0x35,0x3A,0x33,0x30, +0x3A,0x31,0x33,0x20,0x31,0x39,0x39,0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xE9,0x68,0x0F,0x42,0x65,0x47,0x69,0x4E,0x6E,0x49,0x6E,0x47,0x20,0x6F,0x46,0x20, +0x63,0x4F,0x64,0x45,0xCC,0x13,0x5A,0x15,0xE8,0x16,0x76,0x18,0x04,0x1A,0x92,0x1B, +0x20,0x1D,0xAE,0x1E,0x3C,0x20,0xCA,0x21,0x58,0x23,0xE6,0x24,0x74,0x26,0x02,0x28, +0x90,0x29,0x1E,0x2B,0xAC,0x2C,0x3A,0x2E,0xC8,0x2F,0x56,0x31,0xE4,0x32,0x72,0x34, +0x00,0x36,0x8E,0x37,0x1C,0x39,0xAA,0x3A,0x38,0x3C,0xC6,0x3D,0x54,0x3F,0xE2,0x40, +0x70,0x42,0xFE,0x43,0x8C,0x45,0x1A,0x47,0xA8,0x48,0x36,0x4A,0xC4,0x4B,0x52,0x4D, +0xE0,0x4E,0x6E,0x50,0xFC,0x51,0x8A,0x53,0x18,0x55,0xA6,0x56,0x34,0x58,0xC2,0x59, +0x50,0x5B,0xDE,0x5C,0x6C,0x5E,0xFA,0x5F,0x88,0x61,0x16,0x63,0xA4,0x64,0x32,0x66, +0xC0,0x67,0x4E,0x69,0xDC,0x6A,0x6A,0x6C,0xF8,0x6D,0x86,0x6F,0x14,0x71,0xA2,0x72, +0x30,0x74,0xBE,0x75,0x4C,0x77,0x6C,0x77,0x8C,0x77,0xAC,0x77,0x33,0xDB,0x8A,0xDC, +0x53,0x33,0xDB,0x25,0x07,0x00,0x75,0x0A,0x8A,0x1E,0x08,0x01,0x83,0xE3,0x0C,0xEB, +0x20,0x90,0x3C,0x01,0x75,0x0A,0x8A,0x1E,0x08,0x01,0x80,0xE3,0xC0,0xEB,0x12,0x90, +0x8A,0x1E,0x0D,0x01,0x3C,0x02,0x75,0x06,0x80,0xE3,0x0C,0xEB,0x04,0x90,0x80,0xE3, +0xC0,0x53,0x50,0x8B,0x1E,0xBA,0x13,0x8E,0xDB,0xE8,0x4C,0x65,0x55,0x8B,0xEC,0x53, +0x1E,0x2B,0xC0,0x8E,0xD8,0x8B,0x5E,0x04,0xC1,0xE3,0x04,0x03,0x5E,0x06,0xD1,0xE3, +0x2E,0x8B,0x9F,0x44,0x00,0x8D,0x47,0x2A,0x1E,0x5A,0x1F,0x5B,0x5D,0xC3,0x55,0x8B, +0xEC,0x53,0x1E,0x2B,0xC0,0x8E,0xD8,0x8B,0x5E,0x04,0xC1,0xE3,0x04,0x03,0x5E,0x06, +0xD1,0xE3,0x2E,0x8B,0x9F,0x44,0x00,0x8D,0x47,0x34,0x1E,0x5A,0x1F,0x5B,0x5D,0xC3, +0xFB,0x55,0x8B,0xEC,0x53,0x51,0x52,0x56,0x57,0x1E,0x06,0x1E,0x07,0x33,0xC0,0x8E, +0xD8,0x8B,0x5E,0x04,0x26,0x8A,0x47,0x59,0x25,0x03,0x00,0x8B,0xF0,0xD1,0xE6,0x2E, +0x8B,0xB4,0xC4,0x00,0xC1,0xE0,0x04,0x26,0x02,0x47,0x1A,0xD1,0xE0,0x8B,0xE8,0x2E, +0x8B,0xAE,0x44,0x00,0x89,0x2C,0x26,0x8A,0x47,0x1C,0x88,0x44,0x0F,0x26,0x8A,0x47, +0x1D,0x88,0x44,0x10,0x26,0x8A,0x47,0x1E,0x88,0x44,0x11,0x26,0x8A,0x47,0x1F,0x88, +0x44,0x12,0x26,0x8A,0x47,0x20,0x88,0x44,0x13,0x26,0x8A,0x47,0x23,0x88,0x44,0x14, +0x26,0x8A,0x47,0x24,0x88,0x44,0x15,0x26,0x8A,0x47,0x5A,0x88,0x44,0x0E,0x33,0xC0, +0x89,0x44,0x06,0x89,0x44,0x08,0x88,0x44,0x0B,0x88,0x44,0x0A,0xB0,0x21,0xB4,0x64, +0x89,0x44,0x04,0x89,0x44,0x02,0xB0,0x55,0x88,0x44,0x0D,0x88,0x44,0x0C,0xE8,0x6A, +0x00,0x72,0x5B,0xE8,0xC9,0x00,0xE8,0xBD,0x10,0x89,0x44,0x08,0x80,0x7C,0x0F,0x01, +0x74,0x29,0xE8,0x2B,0x02,0xE8,0x7F,0x02,0x80,0x7C,0x0F,0x03,0x74,0x1D,0xE8,0xA5, +0x10,0x8B,0xF8,0x2B,0x44,0x08,0x3D,0xA0,0x0F,0x72,0x10,0x89,0x7C,0x08,0x33,0xC0, +0x87,0x44,0x06,0x85,0xC0,0x75,0x04,0xC6,0x44,0x0A,0xFF,0x8A,0x44,0x0A,0x84,0xC0, +0x75,0x0B,0xB8,0x08,0x00,0xE8,0x4C,0x4A,0xE8,0xA9,0x01,0x73,0xBF,0xE8,0x4F,0x01, +0x81,0x66,0x48,0x7F,0xFF,0x83,0x66,0x7A,0xBF,0xB0,0x02,0xE8,0x00,0x0E,0x8A,0x44, +0x0A,0x98,0x07,0x1F,0x5F,0x5E,0x5A,0x59,0x5B,0x5D,0xC3,0x81,0x4E,0x48,0x80,0x00, +0xB0,0x40,0xE8,0x1F,0x4A,0xE8,0x6B,0x40,0x73,0x2A,0xE8,0x49,0x10,0x8B,0xD8,0xB0, +0x05,0xE8,0x10,0x4A,0xF6,0x46,0x27,0x02,0x75,0x1A,0xE8,0x39,0x10,0x2B,0xC3,0x3D, +0x58,0x1B,0x72,0xEB,0x81,0x66,0x48,0x7F,0xFF,0xB0,0x02,0xE8,0xC0,0x0D,0xC6,0x44, +0x0A,0x01,0xF9,0xC3,0x83,0x4E,0x7A,0x40,0xF8,0xC3,0xFB,0xB0,0x01,0xE8,0xE4,0x49, +0xFA,0xE8,0x95,0x1E,0xE4,0x0A,0x84,0xC0,0x75,0xF0,0xB0,0x4E,0xE6,0x0A,0xFB,0xB0, +0x01,0xE8,0xD0,0x49,0xFA,0xE8,0x81,0x1E,0xE4,0x0A,0x84,0xC0,0x75,0xF0,0xC3,0xFA, +0xE8,0x76,0x1E,0xE4,0xEC,0x88,0x44,0x16,0xE4,0xE4,0x88,0x44,0x17,0xE4,0xF8,0x88, +0x44,0x18,0xE4,0xF0,0x88,0x44,0x19,0xE4,0x10,0x88,0x44,0x1A,0xE4,0x12,0x88,0x44, +0x1B,0xE4,0x14,0x88,0x44,0x1C,0xE4,0x34,0x88,0x44,0x1D,0xE4,0x36,0x88,0x44,0x1E, +0xE4,0xD8,0x24,0x01,0x8A,0xE0,0xE4,0xDA,0x24,0x02,0x0A,0xC4,0x88,0x44,0x1F,0x8A, +0x44,0x10,0xE8,0xC9,0x1F,0x8A,0x44,0x11,0xE8,0x31,0x21,0x8A,0x44,0x12,0xE8,0x85, +0x21,0x8A,0x44,0x13,0xE8,0x3F,0x21,0xC6,0x86,0xA1,0x00,0x00,0xE4,0x14,0x24,0x10, +0xE6,0x14,0xE4,0x12,0x24,0x3D,0xE6,0x12,0x8A,0x44,0x15,0x3C,0x01,0x72,0x1E,0x77, +0x16,0xB0,0x11,0xE6,0x34,0xB0,0x13,0xE6,0x36,0xE4,0x14,0x0C,0x10,0xE6,0x14,0xE4, +0x12,0x0C,0x40,0xE6,0x12,0xEB,0x06,0xE4,0x12,0x0C,0x02,0xE6,0x12,0x8A,0x44,0x0F, +0x3C,0x01,0x74,0x06,0x3C,0x02,0x74,0x0A,0xEB,0x0E,0xE4,0x12,0x0C,0x08,0xE6,0x12, +0xEB,0x06,0xE4,0x12,0x0C,0x10,0xE6,0x12,0xE8,0x2F,0xFF,0x8A,0x44,0x14,0x3C,0x02, +0x75,0x08,0xB0,0x55,0x88,0x44,0x0C,0x88,0x44,0x0D,0xB0,0x21,0xB4,0x64,0x89,0x44, +0x04,0x89,0x44,0x02,0xE4,0x0C,0x0C,0x10,0xE6,0x0C,0xE8,0xCF,0x39,0xFB,0xC3,0xE8, +0x41,0x3F,0x73,0x08,0xFB,0xB0,0x0A,0xE8,0xEA,0x48,0xEB,0xF3,0xFA,0xE8,0x99,0x1D, +0x8A,0x64,0x16,0x8A,0x44,0x17,0x89,0x86,0x94,0x00,0xE6,0xE4,0x8A,0xC4,0xE6,0xEC, +0x8A,0x64,0x18,0x8A,0x44,0x19,0x89,0x86,0x96,0x00,0xE6,0xF0,0x8A,0xC4,0xE6,0xF8, +0x8A,0x44,0x1A,0xE6,0x10,0x8A,0x44,0x1B,0xE6,0x12,0x8A,0x44,0x1C,0xE6,0x14,0x8A, +0x44,0x1D,0xE6,0x34,0x8A,0x44,0x1E,0xE6,0x36,0x8A,0x44,0x1F,0xE6,0xD8,0xE6,0xDA, +0xE9,0xB7,0xFE,0x90,0xFA,0x8A,0x44,0x0E,0xE6,0xFE,0xE4,0x02,0xA8,0x01,0x75,0x05, +0x33,0xC0,0xFB,0xF8,0xC3,0x33,0xC0,0xE4,0x00,0xFB,0xF9,0xC3,0x8A,0x64,0x14,0x80, +0xFC,0x02,0x74,0x2B,0xFE,0xC0,0xFE,0xC7,0x80,0xFF,0x4E,0x72,0x1C,0x74,0x09,0x80, +0xFF,0x50,0x73,0x08,0xB0,0x0A,0xEB,0x17,0xB0,0x0D,0xEB,0x13,0x02,0xDC,0x32,0xFF, +0x80,0xFB,0x7F,0x7C,0x02,0xB3,0x21,0x8A,0xC3,0x3C,0x7F,0x7C,0x02,0xB0,0x21,0xC3, +0xFA,0x80,0x7C,0x0B,0x04,0x76,0x02,0xFB,0xC3,0x8B,0x46,0x24,0x3D,0x08,0x00,0x72, +0xF6,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x8A,0x44,0x0C,0x8B,0x5C,0x02,0xAA,0xE8,0xAB, +0xFF,0xAA,0xE8,0xA7,0xFF,0xAA,0xE8,0xA3,0xFF,0xAA,0xE8,0x9F,0xFF,0x88,0x44,0x0C, +0x89,0x5C,0x02,0x80,0x44,0x0B,0x04,0x89,0x7E,0x22,0x83,0x6E,0x24,0x04,0x83,0x46, +0x1A,0x04,0x80,0x7E,0x26,0x02,0x74,0x06,0x80,0x66,0x26,0xFD,0xFB,0xC3,0x60,0xB0, +0xFD,0xE8,0xE4,0x3E,0x61,0xFB,0xC3,0xFA,0x80,0x7C,0x0F,0x03,0x75,0x09,0xC6,0x44, +0x0B,0x00,0xE8,0xC7,0x38,0xFB,0xC3,0xC4,0x7E,0x14,0x8B,0x4E,0x3A,0x85,0xC9,0x75, +0x35,0x26,0x8B,0x0D,0x47,0x47,0xE3,0xEA,0x3B,0x7E,0x04,0x76,0x22,0xB8,0x02,0x00, +0x39,0x46,0x2E,0x77,0x07,0xC7,0x46,0x2E,0x00,0x00,0xEB,0x13,0x8B,0x5E,0x2C,0x89, +0x5E,0x04,0x26,0xC7,0x07,0x00,0x00,0x43,0x43,0x89,0x5E,0x2C,0x29,0x46,0x2E,0x85, +0xC9,0x78,0xCE,0x89,0x4E,0x3A,0x8A,0x44,0x0D,0x8B,0x5C,0x04,0x26,0x8A,0x25,0x47, +0x3A,0xC4,0x75,0x16,0xFE,0x4C,0x0B,0xFF,0x44,0x06,0xE8,0x0F,0xFF,0xE2,0xED,0x88, +0x44,0x0D,0x89,0x5C,0x04,0x89,0x4E,0x3A,0xEB,0xA7,0xC6,0x44,0x0A,0xFE,0xE8,0x5B, +0x38,0xFB,0xC3,0x90,0xE8,0xAF,0x0D,0x8A,0xE8,0x8A,0x0E,0xCB,0x13,0xB3,0x07,0x8A, +0xC1,0xEE,0xEB,0x00,0xEC,0x3A,0xC1,0x75,0x09,0x02,0xCD,0xFE,0xCB,0x75,0xF0,0xEB, +0x0C,0x90,0x88,0x0E,0xCB,0x13,0x8A,0xE8,0xBB,0xFF,0xFF,0xF9,0xC3,0x88,0x0E,0xCB, +0x13,0xF8,0xC3,0x90,0xBB,0x3F,0x3F,0x8A,0x8E,0x9E,0x00,0xBA,0xFE,0x00,0xEC,0x8A, +0xE8,0x32,0xC1,0x22,0xC3,0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x90,0xE8,0xE5,0xFF,0x73, +0x01,0xC3,0xBA,0xD0,0x00,0xBB,0x03,0x03,0x8A,0x8E,0x9F,0x00,0xEC,0x8A,0xE8,0x32, +0xC1,0x22,0xC3,0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x90,0x33,0xC0,0x8E,0xD8,0x8E,0xC0, +0x80,0x3E,0xC8,0x13,0x00,0x75,0x07,0xB0,0x0A,0xE8,0x08,0x47,0xEB,0xF2,0xFB,0x33, +0xDB,0x8A,0x1E,0xC9,0x13,0x43,0x43,0x83,0xFB,0x7E,0x76,0x07,0x33,0xDB,0xB0,0x02, +0xE8,0xF1,0x46,0x2E,0x8B,0xAF,0x44,0x00,0x83,0x7E,0x08,0x00,0x74,0xE7,0x88,0x1E, +0xC9,0x13,0xB0,0x02,0xE8,0xDD,0x46,0xFA,0xF7,0x46,0x38,0x40,0x00,0x74,0x14,0xE8, +0x92,0x1B,0xE8,0x7F,0xFF,0x72,0x1C,0x33,0xD2,0x8A,0x96,0x9F,0x00,0x83,0xC2,0x0E, +0xEB,0x0C,0x90,0xE8,0x73,0x1B,0xE8,0x83,0xFF,0x72,0x08,0xBA,0x48,0x00,0xE8,0x33, +0xFF,0x73,0xAB,0x23,0xCB,0x89,0x8E,0x9A,0x00,0x89,0x96,0x9C,0x00,0xFE,0x86,0xB5, +0x00,0xC6,0x06,0xC8,0x13,0x00,0xB0,0x0A,0xE8,0x63,0x0A,0xFB,0xEB,0x89,0x10,0x18, +0x08,0x28,0x33,0xC0,0xA0,0x05,0x01,0x8A,0xC8,0x24,0x40,0x75,0x24,0xC7,0x06,0x7C, +0x12,0x70,0x45,0xC7,0x06,0x42,0x12,0x01,0x00,0xC6,0x06,0x54,0x12,0x02,0xB0,0x08, +0xF6,0xC1,0x01,0x74,0x02,0xB0,0x04,0xA3,0x46,0x12,0xA2,0x4C,0x12,0xA2,0x94,0x12, +0xC3,0xC7,0x06,0x7C,0x12,0x98,0x45,0xA0,0x0F,0x01,0x84,0xC0,0x75,0x0E,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x1E,0x9C,0x0E,0xE8,0xAD,0x0C,0x90,0xC7,0x06,0x44,0x12, +0x01,0x00,0xA3,0x42,0x12,0x8B,0xD8,0xC1,0xE3,0x04,0x88,0x1E,0x94,0x12,0xBE,0xE2, +0x05,0x2B,0xF0,0x8B,0xC8,0x33,0xDB,0x8B,0xFB,0x2E,0xAC,0x88,0x85,0x48,0x12,0x8A, +0xD8,0x0C,0x05,0xE6,0xFE,0x8A,0xE0,0xEB,0x00,0xE4,0xFE,0x32,0xC4,0xA8,0x3F,0x74, +0x03,0xE9,0x9A,0x00,0xE4,0x00,0x88,0x85,0x50,0x12,0x8A,0xE0,0x24,0x30,0xBA,0x10, +0xFF,0x3C,0x30,0x74,0x12,0x80,0xFC,0x04,0x74,0x0A,0xBA,0x04,0x03,0xF6,0x06,0x08, +0x01,0xFE,0x74,0x03,0xBA,0x08,0x0F,0x88,0x95,0x4C,0x12,0x02,0xFA,0x32,0xC0,0xF6, +0xC4,0x08,0x74,0x02,0xB0,0x01,0x88,0x85,0x58,0x12,0x8A,0xC4,0x3C,0x35,0x74,0x57, +0x3C,0x34,0x74,0x53,0x3C,0x04,0x74,0x4F,0x3C,0x14,0x74,0x4B,0x3C,0x15,0x74,0x47, +0xA8,0x40,0x74,0x25,0xC6,0x85,0x54,0x12,0x04,0xD1,0xE7,0xB4,0x03,0x8A,0xC3,0x89, +0x85,0x5C,0x12,0x8A,0xC3,0x8A,0xE3,0x80,0xCC,0x01,0x89,0x85,0x64,0x12,0xD1,0xEF, +0x47,0xE2,0x03,0xEB,0x1A,0x90,0xE9,0x70,0xFF,0xC6,0x85,0x54,0x12,0x02,0xD1,0xE7, +0x8A,0xE6,0x8A,0xC3,0x0C,0x04,0x89,0x85,0x5C,0x12,0xD1,0xEF,0x47,0xE2,0xE7,0x33, +0xC0,0x8A,0xC7,0xA3,0x46,0x12,0xC3,0xC6,0x85,0x54,0x12,0x06,0xEB,0xBB,0xC6,0x85, +0x54,0x12,0x00,0x33,0xC0,0x88,0x85,0x50,0x12,0x88,0x85,0x4C,0x12,0x88,0x85,0x58, +0x12,0xEB,0xA6,0xC7,0x46,0x26,0x02,0x12,0x8B,0x46,0x1E,0x89,0x46,0x00,0x89,0x46, +0x22,0x8B,0x46,0x20,0x89,0x46,0x24,0xC7,0x46,0x1A,0x00,0x00,0xC3,0xC7,0x46,0x3C, +0x80,0x00,0xC7,0x46,0x38,0x01,0x00,0x1E,0x56,0x8B,0x76,0x30,0x89,0x76,0x04,0x89, +0x76,0x14,0x8E,0x5E,0x06,0x33,0xC0,0x89,0x04,0x46,0x46,0x89,0x76,0x2C,0x89,0x46, +0x3A,0x8B,0x46,0x32,0x48,0x48,0x89,0x46,0x2E,0x5E,0x1F,0xC3,0x33,0xC0,0x89,0x46, +0x48,0x89,0x46,0x4A,0xC7,0x46,0x46,0xAE,0x01,0x89,0x46,0x4E,0x8B,0x46,0x44,0x89, +0x46,0x50,0x8B,0x46,0x42,0x89,0x46,0x40,0x89,0x46,0x08,0xC3,0x33,0xC0,0x89,0x46, +0x76,0x89,0x46,0x78,0xC7,0x46,0x7A,0x10,0x00,0x56,0x1E,0x8B,0x76,0x70,0x89,0x76, +0x10,0x89,0x76,0x0C,0x8E,0x5E,0x12,0xC7,0x04,0x00,0x00,0x8B,0x46,0x72,0x89,0x46, +0x74,0x1F,0x5E,0xC3,0x89,0x56,0x18,0x89,0x56,0x02,0x89,0x56,0x06,0x89,0x56,0x0A, +0x89,0x56,0x0E,0x89,0x56,0x12,0x89,0x56,0x16,0x8B,0xD8,0x4B,0x4B,0xC1,0xE3,0x02, +0xBF,0x02,0x00,0x89,0x7E,0x1E,0x03,0xFB,0x89,0x7E,0x30,0x03,0xFB,0x89,0x7E,0x42, +0x03,0xFB,0x89,0x7E,0x70,0x83,0xEB,0x08,0x89,0x5E,0x20,0x89,0x5E,0x32,0x89,0x5E, +0x44,0x89,0x5E,0x72,0x50,0xE8,0x2B,0xFF,0xE8,0x71,0xFF,0xE8,0x3F,0xFF,0xE8,0x8B, +0xFF,0x58,0xC3,0xB8,0x10,0x75,0xC1,0xE8,0x04,0x0E,0x5B,0x03,0xC3,0xA3,0xBA,0x13, +0x83,0x3E,0x42,0x12,0x00,0x74,0x07,0x80,0x3E,0x94,0x12,0x00,0x75,0x0E,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x1E,0x9C,0x0E,0xE8,0xBD,0x0A,0x90,0xB8,0x30,0x7A,0xC1, +0xE8,0x04,0x40,0xA3,0xC0,0x13,0x2B,0x06,0x12,0x01,0xF7,0xD8,0x33,0xD2,0x8B,0xCA, +0x8A,0x0E,0x94,0x12,0xF7,0xF1,0x3D,0x80,0x00,0x77,0x0E,0x6A,0x00,0x1F,0xC6,0x06, +0x93,0x12,0x25,0x9C,0x0E,0xE8,0x90,0x0A,0x90,0x48,0x3D,0xFF,0x07,0x72,0x03,0xB8, +0xFF,0x07,0xA3,0xC2,0x13,0x33,0xC9,0x8A,0x0E,0x94,0x12,0x33,0xF6,0xB8,0x00,0x09, +0x2E,0x8B,0xAC,0x44,0x00,0x89,0x46,0x4C,0x40,0x46,0x46,0xE2,0xF3,0x8A,0x0E,0x94, +0x12,0x33,0xF6,0x8B,0x16,0xC0,0x13,0xA1,0xC2,0x13,0x2E,0x8B,0xAC,0x44,0x00,0xE8, +0x22,0xFF,0x03,0xD0,0x46,0x46,0xE2,0xF2,0xC3,0x33,0xC0,0x2E,0x8B,0xAD,0x44,0x00, +0x89,0x46,0x08,0x47,0x47,0xE2,0xF4,0xC3,0x51,0x33,0xC0,0x0A,0xC2,0x2E,0x8B,0xAD, +0x44,0x00,0x89,0x86,0x9E,0x00,0x81,0x4E,0x38,0x00,0x20,0x47,0x47,0xFE,0xC4,0x80, +0xFC,0x04,0x72,0x04,0x32,0xE4,0xFE,0xC0,0xE2,0xE3,0x59,0x83,0xE9,0x10,0x74,0x05, +0xF7,0xD9,0xE8,0xC4,0xFF,0xC3,0x51,0x33,0xC0,0x0A,0xC2,0x2E,0x8B,0xAD,0x44,0x00, +0x89,0x86,0x9E,0x00,0x83,0x4E,0x38,0x40,0x47,0x47,0x80,0xC4,0x10,0x79,0x04,0x32, +0xE4,0xFE,0xC0,0xE2,0xE6,0x59,0x83,0xE9,0x10,0x74,0x05,0xF7,0xD9,0xE8,0x99,0xFF, +0xC3,0xE8,0xD2,0xFF,0xC3,0x89,0x08,0x98,0x08,0xC6,0x08,0xF1,0x08,0x8B,0x0E,0x42, +0x12,0x33,0xF6,0x51,0x56,0x33,0xDB,0x8B,0xCB,0x8A,0x94,0x48,0x12,0x8A,0x8C,0x4C, +0x12,0x8A,0x9C,0x54,0x12,0x8B,0xFE,0xC1,0xE7,0x05,0x85,0xDB,0x75,0x02,0xB1,0x10, +0x2E,0xFF,0x97,0xF5,0x08,0x5E,0x59,0x46,0xE2,0xD9,0xC3,0x01,0xCC,0x03,0xD0,0x00, +0xE8,0x02,0xD0,0x00,0xE8,0x01,0xD0,0x00,0xE8,0x00,0xD0,0x00,0xE8,0x04,0xD0,0xA8, +0xDA,0x00,0xDC,0x00,0xDE,0x01,0xD8,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x04,0xD0,0xA8, +0xDA,0x20,0xDC,0x00,0xDE,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x00,0xD8,0x03,0xCC,0x03, +0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03, +0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x04,0xD0,0x00, +0xDA,0x20,0xDC,0x03,0xDE,0x01,0xD8,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x03,0xCC,0x00, +0xD8,0x00,0xCC,0x00,0xD0,0x00,0x00,0x56,0x52,0x1E,0x0E,0x1F,0xBE,0x2B,0x09,0x33, +0xD2,0xFC,0xAD,0x85,0xC0,0x74,0x0D,0x8A,0xD4,0xEE,0xAD,0x85,0xC0,0x74,0x05,0x8A, +0xD4,0xEE,0xEB,0xEE,0x1F,0x5A,0x5E,0xC3,0xE4,0x80,0x84,0xC0,0x74,0x16,0x78,0x14, +0xB0,0x27,0xE6,0xFC,0xB0,0x11,0xE6,0x34,0xE4,0xFC,0x3C,0x27,0x75,0x06,0xE4,0x11, +0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x83,0xEA,0x02,0xB0, +0x10,0xEE,0x88,0x86,0xAF,0x00,0xB0,0x11,0x83,0xC2,0x04,0xEE,0x83,0xC2,0x02,0xEE, +0xB0,0x13,0x83,0xC2,0x02,0xEE,0x83,0xC2,0x02,0xEE,0x2E,0xA1,0x2E,0x2D,0x89,0x86, +0x94,0x00,0x83,0xEA,0x0E,0xEE,0x83,0xC2,0x02,0x8A,0xC4,0xEE,0x83,0xC2,0x04,0xB0, +0x03,0xEE,0x88,0x86,0xA8,0x00,0x83,0xEA,0x04,0x32,0xC0,0xEE,0x83,0xC2,0x02,0xB0, +0x89,0xEE,0x88,0x86,0xA6,0x00,0x0C,0x06,0xEE,0xB0,0x40,0xB4,0x38,0x89,0x46,0x1C, +0xC7,0x46,0x36,0x38,0x00,0x83,0xC2,0x04,0x32,0xC0,0xEE,0x88,0x86,0xA7,0x00,0xC3, +0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x83,0xEA,0x02,0xEC,0x3A,0x86,0xAF,0x00,0x75,0x24, +0x83,0xC2,0x04,0xEC,0x3C,0x11,0x75,0x1C,0x83,0xC2,0x06,0xEC,0x3C,0x13,0x75,0x14, +0x83,0xEA,0x08,0x8A,0x86,0xA8,0x00,0xEE,0x83,0xEA,0x02,0xEC,0x24,0xC0,0x3C,0xC0, +0x75,0x02,0xF8,0xC3,0xF9,0xC3,0x33,0xC9,0x8B,0xD1,0x8B,0xF1,0x8A,0x0E,0x94,0x12, +0xC1,0xE9,0x02,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x0E,0x8A, +0x86,0x9E,0x00,0xE6,0xFE,0x32,0xC0,0xE6,0x80,0x42,0xE8,0xFA,0xFE,0x83,0xC6,0x08, +0xE2,0xE1,0x85,0xD2,0x74,0x03,0xE8,0x05,0x08,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E, +0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x06,0xE8,0x73, +0x16,0xE8,0x12,0xFF,0x46,0x46,0xE2,0xEA,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94, +0x12,0xC1,0xE9,0x02,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x16, +0xE8,0x46,0x16,0xE8,0xD2,0xFE,0x73,0x0E,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x1C, +0x9C,0x0E,0xE8,0xE3,0x07,0x90,0x83,0xC6,0x08,0xE2,0xD9,0xC3,0x33,0xC9,0x8B,0xF1, +0x8A,0x0E,0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x16, +0xE8,0x21,0x16,0xE8,0x2A,0xFF,0x73,0x0E,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x1C, +0x9C,0x0E,0xE8,0xB3,0x07,0x90,0x46,0x46,0xE2,0xDA,0xC3,0x0C,0x00,0x00,0x10,0x00, +0x13,0x12,0x00,0x00,0x14,0x00,0x28,0x3C,0x00,0x1B,0x3E,0x00,0x00,0x2A,0x00,0x00, +0x2C,0x00,0x00,0x42,0x00,0x14,0xD8,0x00,0x00,0xDA,0x00,0x00,0x34,0x00,0x11,0x36, +0x00,0x13,0x38,0x00,0x11,0x3A,0x00,0x13,0x00,0x00,0x56,0x50,0x52,0xBE,0x2B,0x0B, +0x2E,0xAD,0x85,0xC0,0x74,0x06,0x92,0x2E,0xAC,0xEE,0xEB,0xF4,0x5A,0x58,0x5E,0xC3, +0x53,0x2E,0xA1,0x5C,0x22,0xE6,0xE4,0xE6,0xF0,0x8A,0xC4,0xE6,0xEC,0xE6,0xF8,0xE8, +0xD8,0xFF,0xB0,0x4B,0xE6,0x10,0xB0,0x50,0xE6,0x12,0xB0,0x38,0xE6,0x14,0xE8,0xAE, +0x15,0xB0,0x46,0xE6,0x0A,0xE8,0xA7,0x15,0xB0,0x1A,0xE6,0x0A,0xE8,0xA0,0x15,0xB0, +0x22,0xE6,0x0A,0xE8,0x99,0x15,0xE8,0xFD,0x06,0x8B,0xD8,0xE4,0x16,0xA8,0x04,0x75, +0x18,0xE8,0xF2,0x06,0x2B,0xC3,0x3D,0x32,0x00,0x72,0xF0,0x6A,0x00,0x1F,0xC6,0x06, +0x93,0x12,0x23,0x9C,0x0E,0xE8,0x10,0x07,0x90,0xE8,0xDA,0x06,0x2B,0xC3,0x3D,0x24, +0x00,0x77,0x1B,0xB0,0x31,0xE6,0xFC,0x56,0x51,0x55,0xB9,0x10,0x00,0x2E,0x8B,0xAC, +0x44,0x00,0x81,0x4E,0x38,0x80,0x00,0x46,0x46,0xE2,0xF2,0x5D,0x59,0x5E,0xE8,0x69, +0xFF,0xE8,0x4B,0x15,0xB0,0x46,0xE6,0x0A,0xE8,0x44,0x15,0x5B,0xC3,0x33,0xF6,0x8B, +0x0E,0x42,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x06,0xE8, +0x17,0x15,0xE8,0x5B,0xFF,0x83,0xC6,0x20,0xE2,0xE9,0xC3,0x8B,0xC2,0x05,0x04,0x00, +0x89,0x46,0x28,0x2E,0xA1,0x2E,0x2D,0x89,0x86,0x8E,0x00,0x89,0x86,0x90,0x00,0x89, +0x86,0x92,0x00,0xC6,0x86,0xA3,0x00,0x0A,0xC6,0x86,0xC3,0x00,0x03,0x52,0x83,0xC2, +0x04,0x8A,0x86,0xA6,0x00,0x0C,0x06,0xEE,0x5A,0x83,0xC2,0x02,0xB0,0x05,0xEE,0x88, +0x86,0xA5,0x00,0xC3,0xE8,0x03,0xFF,0xE8,0xE5,0x14,0xB0,0x42,0xE6,0x0A,0xF7,0x46, +0x38,0x80,0x00,0x74,0x06,0x2E,0xA1,0x98,0x22,0xEB,0x04,0x2E,0xA1,0x68,0x22,0xC7, +0x46,0x1C,0x0C,0x00,0x89,0x86,0x94,0x00,0x89,0x86,0x96,0x00,0x89,0x86,0x8E,0x00, +0x89,0x86,0x90,0x00,0x89,0x86,0x92,0x00,0xE6,0xF0,0xE6,0xE4,0x8A,0xC4,0xE6,0xF8, +0xE6,0xEC,0xC6,0x86,0xC3,0x00,0x03,0xE8,0xA5,0x14,0xB0,0x1A,0xE6,0x0A,0xB0,0x10, +0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94,0x12,0x2E, +0x8B,0xAC,0x44,0x00,0xF7,0x46,0x38,0x40,0x00,0x74,0x06,0xE8,0x76,0x14,0xE8,0x5A, +0xFF,0x46,0x46,0xE2,0xEA,0xC3,0x33,0xC9,0x8B,0xF1,0x8A,0x0E,0x94,0x12,0x2E,0x8B, +0xAC,0x44,0x00,0xF7,0x46,0x38,0x00,0x20,0x74,0x06,0xE8,0x4C,0x14,0xE8,0x74,0xFF, +0x46,0x46,0xE2,0xEA,0xC3,0x90,0x83,0x3E,0x44,0x12,0x00,0x75,0x14,0xB0,0x01,0xBA, +0x06,0x01,0xEE,0x2A,0xC0,0xEE,0xB0,0x02,0xEE,0xB0,0x04,0xEE,0xB8,0x00,0x02,0xEB, +0x0F,0xBA,0x06,0x01,0xB0,0x40,0xEE,0xB8,0x01,0x00,0x8A,0x0E,0x0E,0x01,0xD3,0xE0, +0xA3,0x88,0x12,0xC3,0xA1,0x88,0x12,0xA3,0x84,0x12,0x2D,0x20,0x00,0xA3,0x8A,0x12, +0x2D,0x20,0x00,0xA3,0x82,0x12,0xC7,0x06,0x86,0x12,0x20,0x00,0xC7,0x06,0x80,0x12, +0x32,0x00,0xC3,0x83,0x3E,0x44,0x12,0x00,0x74,0x76,0x8B,0x0E,0x42,0x12,0x33,0xF6, +0x8A,0xA4,0x54,0x12,0x84,0xE4,0x74,0x5F,0x8A,0x84,0x48,0x12,0x0C,0x04,0xE6,0xFE, +0xF6,0xC4,0x04,0x74,0x25,0xB0,0x1B,0xBA,0x00,0x00,0xEE,0xEB,0x00,0x2A,0xC0,0xBA, +0x02,0x00,0xEE,0xEB,0x00,0xB0,0x03,0xEE,0xEB,0x00,0x32,0xC0,0xBA,0x02,0x00,0xEE, +0xEB,0x00,0xBA,0x00,0x00,0xB0,0x00,0xEE,0xEB,0x2D,0xB0,0x1F,0xBA,0x00,0x00,0xEE, +0xEB,0x00,0x2A,0xC0,0xBA,0x02,0x00,0xEE,0xEB,0x00,0xB0,0x03,0xEE,0xEB,0x00,0xD1, +0xE6,0x8A,0x84,0x5D,0x12,0xD1,0xEE,0xF6,0xD0,0xBA,0x02,0x00,0xEE,0xEB,0x00,0xBA, +0x00,0x00,0xB0,0x0A,0xEE,0xEB,0x00,0xE4,0x04,0xEB,0x00,0xE4,0x04,0x46,0xE2,0x90, +0xC3,0x90,0xB8,0x14,0x00,0xBA,0x3E,0xFF,0xEF,0xB8,0x06,0x00,0xBA,0x32,0xFF,0xEF, +0xB8,0x0F,0x00,0xBA,0x34,0xFF,0xEF,0xBA,0x36,0xFF,0xEF,0x83,0x3E,0x44,0x12,0x00, +0x75,0x16,0xB8,0x11,0x00,0xBA,0x38,0xFF,0xEF,0xB8,0x12,0x00,0xBA,0x3A,0xFF,0xEF, +0xB8,0x1B,0x00,0xBA,0x3C,0xFF,0xEF,0xC3,0xB8,0x11,0x00,0xBA,0x38,0xFF,0xEF,0xB8, +0x12,0x00,0xBA,0x3A,0xFF,0xEF,0xB8,0x1B,0x00,0xBA,0x3C,0xFF,0xEF,0xC3,0xB8,0xFC, +0x00,0xBA,0x28,0xFF,0xEF,0xFB,0x83,0x3E,0x44,0x12,0x00,0x74,0x07,0xB8,0xCC,0x00, +0xBA,0x28,0xFF,0xEF,0xC3,0x00,0xFF,0xFF,0x20,0x24,0x28,0xFF,0x2C,0xFF,0xFF,0x30, +0x34,0x38,0xFF,0xFF,0x3C,0x90,0x3C,0x0F,0x77,0x0E,0xBB,0x15,0x0E,0x2E,0xD7,0x3C, +0xFF,0x74,0x05,0x8A,0xD8,0xF8,0xC3,0x90,0x2A,0xDB,0xF9,0xC3,0x83,0x3E,0x44,0x12, +0x00,0x74,0x27,0xA0,0x06,0x01,0x80,0x26,0x06,0x01,0x30,0x80,0x3E,0x06,0x01,0x30, +0x75,0x18,0xB9,0x02,0x00,0xBF,0xC4,0x13,0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xF8, +0xBA,0x04,0x01,0xED,0xAB,0xE2,0xF1,0xEB,0x16,0x90,0xB9,0x04,0x00,0xBF,0xC4,0x13, +0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xF8,0xBA,0x04,0x01,0xEC,0xAA,0xE2,0xF1,0xFA, +0x90,0xBE,0xC4,0x13,0xAD,0x80,0xE4,0x3F,0x80,0xFC,0x02,0x74,0x0E,0x6A,0x00,0x1F, +0xC6,0x06,0x93,0x12,0x0A,0x9C,0x0E,0xE8,0x3E,0x04,0x90,0xAD,0x3C,0x0F,0x75,0xED, +0x8A,0xC4,0xE8,0x81,0xFF,0x72,0xE6,0x88,0x1E,0x1A,0x01,0xC6,0x06,0x8E,0x12,0x00, +0xB0,0x00,0x0A,0x06,0x1A,0x01,0xBA,0x00,0x01,0xEE,0xC6,0x06,0x8F,0x12,0x40,0x83, +0x3E,0x44,0x12,0x00,0x75,0x06,0xB8,0x0C,0x00,0xEB,0x04,0x90,0xB8,0x4C,0x00,0xBA, +0x28,0xFF,0xEF,0xC3,0x83,0x3E,0x44,0x12,0x00,0x75,0x01,0xC3,0xA1,0x50,0x12,0x0B, +0x06,0x52,0x12,0x0A,0xC4,0xA8,0x08,0x74,0xF2,0xA0,0x0F,0x01,0x2A,0xE4,0x50,0xFF, +0x36,0xBA,0x13,0x1F,0xE8,0x36,0x56,0x83,0xC4,0x02,0x6A,0x00,0x1F,0x33,0xC0,0xA3, +0xBC,0x13,0xA0,0x0F,0x01,0xA3,0xBE,0x13,0x8B,0x1E,0xBC,0x13,0x8A,0x87,0x50,0x12, +0xF6,0x87,0x50,0x12,0x08,0x74,0x0D,0x24,0x07,0x8A,0xE0,0xBE,0xCC,0x00,0xA0,0xBC, +0x13,0xE8,0x7A,0x3D,0xFF,0x06,0xBC,0x13,0xFF,0x0E,0xBE,0x13,0x75,0xDA,0xC3,0x90, +0x1E,0x33,0xC0,0x8E,0xD8,0xB0,0x01,0xE8,0x3A,0x3D,0x1F,0xC3,0x33,0xC9,0x8B,0xF1, +0x8A,0x0E,0x94,0x12,0x2E,0x8B,0xAC,0x44,0x00,0xC7,0x46,0x62,0x1A,0x44,0xC7,0x46, +0x7C,0xDE,0x3B,0xC7,0x46,0x7E,0xC4,0x3B,0xC7,0x86,0x80,0x00,0xCE,0x3C,0xE8,0xAB, +0x16,0xC6,0x86,0xC0,0x00,0x11,0x83,0x7E,0x08,0x00,0x74,0x07,0x51,0x56,0xE8,0x19, +0x33,0x5E,0x59,0x46,0x46,0xE2,0xCD,0xC3,0x33,0xC9,0x8B,0xF1,0x8B,0xF9,0x8A,0x0E, +0x94,0x12,0xC1,0xE9,0x02,0xE3,0x13,0x2E,0x8B,0xAC,0x44,0x00,0x8A,0x86,0x9E,0x00, +0x88,0x85,0x6C,0x12,0x83,0xC6,0x08,0x47,0xE2,0xED,0xC3,0xFA,0xFC,0xB0,0xC0,0xBA, +0x00,0x01,0xEE,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8E,0xD0,0xBF,0x16,0x01,0xB9,0xCC, +0x77,0x2B,0xCF,0xD1,0xE9,0xF3,0xAB,0xBC,0x40,0x12,0xE8,0xD9,0x02,0xE8,0x56,0x3C, +0xBE,0xC8,0x0F,0xE8,0xD8,0x3C,0xF4,0x90,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8E,0xD0, +0xF6,0x06,0x0A,0x01,0x80,0x74,0x0B,0xBE,0x17,0x55,0xE8,0xC1,0x3C,0xB0,0x01,0xE8, +0x92,0x3C,0xE8,0xB3,0x00,0xE8,0xFA,0xF5,0xE8,0x08,0xF8,0xE8,0x0F,0xF9,0xE8,0x85, +0xFA,0xE8,0xB6,0xFA,0xE8,0xEF,0xFC,0xE8,0xC2,0x10,0xE8,0xE9,0x3B,0xE8,0xB2,0xFD, +0xE8,0x30,0xFD,0xE8,0x54,0x02,0xC6,0x06,0x8F,0x12,0xC0,0xE8,0xBB,0xFA,0xE8,0xEB, +0xFA,0xE8,0xE9,0xFB,0xE8,0xAF,0xFC,0xE8,0x8D,0xFC,0xE8,0x1F,0xFF,0xE8,0x58,0xFF, +0xE8,0xDB,0xFD,0xE8,0x16,0xFE,0x33,0xC0,0xBE,0x5A,0x05,0xE8,0x70,0x3C,0xE8,0xA3, +0xFE,0xE8,0xE0,0xFC,0xFB,0xBE,0x86,0x44,0xE8,0x63,0x3C,0xE9,0xB0,0x2D,0x56,0x98, +0x8B,0xF0,0x8B,0x42,0x52,0x85,0xC0,0x75,0x27,0xC7,0x42,0x52,0x01,0x00,0x53,0x36, +0x8B,0x9C,0x2C,0x01,0xF6,0xC3,0x01,0x75,0x0C,0x36,0x89,0x68,0x52,0x36,0x89,0xAC, +0x2C,0x01,0x5B,0x5E,0xC3,0x36,0x89,0xAC,0x2C,0x01,0x36,0x89,0xAC,0x1C,0x01,0x5B, +0x5E,0xC3,0x56,0x98,0x8B,0xF0,0x33,0xED,0x36,0x8B,0x84,0x1C,0x01,0xA8,0x01,0x75, +0x15,0x8B,0xE8,0x33,0xC0,0x87,0x42,0x52,0x36,0x89,0x84,0x1C,0x01,0xA8,0x01,0x74, +0x05,0x36,0x89,0x84,0x2C,0x01,0x5E,0xC3,0x56,0x51,0x33,0xF6,0xB8,0x01,0x00,0xB9, +0x08,0x00,0x89,0x84,0x1C,0x01,0x89,0x84,0x2C,0x01,0x46,0x46,0xE2,0xF4,0x59,0x5E, +0xC3,0x90,0xBB,0x01,0x00,0x8B,0xE8,0xFF,0x4E,0x6E,0x74,0x0A,0x8B,0xDD,0x8B,0x46, +0x58,0xA8,0x01,0x74,0xF0,0xC3,0x8B,0x46,0x48,0xA9,0x08,0x00,0x74,0x45,0xF7,0x46, +0x38,0x40,0x00,0x74,0x27,0xE8,0x5C,0x10,0x80,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x24, +0xBF,0x88,0x86,0xA8,0x00,0xEE,0x60,0xB0,0xFE,0xE8,0x6C,0x32,0x61,0xB0,0x02,0xE8, +0x4C,0xFF,0x8B,0x46,0x48,0x24,0xF7,0x89,0x46,0x48,0xEB,0x17,0xE8,0x2A,0x10,0x81, +0x4E,0x26,0x00,0x40,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x8B,0x46,0x48,0xA9,0x04,0x00,0x74,0x14,0xB0,0x02,0xE8,0x21,0xFF,0x8B,0x46,0x48, +0x24,0xFB,0x89,0x46,0x48,0x60,0xB0,0xDF,0xE8,0x2D,0x32,0x61,0x33,0xC0,0x87,0x46, +0x58,0xF6,0xC3,0x01,0x75,0x0B,0x36,0x89,0x47,0x58,0xA8,0x01,0x75,0x0D,0xE9,0x74, +0xFF,0xA3,0x22,0x01,0xA8,0x01,0x75,0x03,0xE9,0x6A,0xFF,0x89,0x1E,0x32,0x01,0xC3, +0xBB,0x01,0x00,0x8B,0xE8,0xF7,0x46,0x38,0x40,0x00,0x74,0x15,0xE8,0xD5,0x0F,0x80, +0xC2,0x0A,0xEC,0xA8,0x40,0x75,0x0A,0x8B,0xDD,0x8B,0x46,0x56,0xA8,0x01,0x74,0xE3, +0xC3,0x8B,0x46,0x26,0x80,0xE4,0xFE,0x80,0xCC,0x02,0x89,0x46,0x26,0xB0,0x02,0xE8, +0xBC,0xFE,0x33,0xC0,0x87,0x46,0x56,0xF6,0xC3,0x01,0x75,0x0A,0x36,0x89,0x47,0x56, +0xA8,0x01,0x75,0x0B,0xEB,0xBD,0xA3,0x20,0x01,0xA8,0x01,0x75,0x02,0xEB,0xB4,0x89, +0x1E,0x30,0x01,0xC3,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA0,0x90,0x12,0x84,0xC0, +0x75,0x49,0xA1,0x22,0x01,0xA8,0x01,0x75,0x03,0xE8,0xF6,0xFE,0xA1,0x20,0x01,0xA8, +0x01,0x75,0x03,0xE8,0x8A,0xFF,0xA1,0xAC,0x13,0x48,0x78,0x05,0x74,0x45,0xA3,0xAC, +0x13,0xA1,0xAE,0x13,0x48,0x78,0x05,0x74,0x51,0xA3,0xAE,0x13,0xA1,0xB0,0x13,0x48, +0x78,0x05,0x74,0x63,0xA3,0xB0,0x13,0xA1,0x7E,0x12,0x40,0x78,0x03,0xA3,0x7E,0x12, +0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0xA0,0x91,0x12,0x40,0x3C, +0x02,0x72,0x0B,0x33,0xC0,0xA2,0x91,0x12,0xFF,0x16,0x7C,0x12,0xEB,0xA4,0xA2,0x91, +0x12,0xEB,0x9F,0xA0,0x8E,0x12,0x32,0x06,0x8F,0x12,0xA2,0x8E,0x12,0x0A,0x06,0x1A, +0x01,0xBA,0x00,0x01,0xEE,0xB8,0x2C,0x01,0xEB,0xA4,0x83,0x3E,0x84,0x12,0x10,0x72, +0x11,0xBA,0x28,0xFF,0xED,0x0C,0x81,0xEF,0xE8,0x39,0x37,0xBA,0x28,0xFF,0xED,0x24, +0x7E,0xEF,0xB8,0x04,0x00,0xEB,0x92,0xC6,0x06,0x8D,0x12,0x01,0xE8,0x25,0x37,0xC6, +0x06,0x8D,0x12,0x00,0xA1,0xB2,0x13,0xEB,0x8B,0x90,0x8A,0x1E,0x0B,0x01,0x2A,0xFF, +0x6B,0xC3,0x19,0xBA,0x62,0xFF,0xEF,0xB8,0x0A,0x00,0xBA,0x60,0xFF,0xEF,0xB8,0x01, +0xE0,0xBA,0x66,0xFF,0xEF,0xB8,0xFF,0xFF,0xBA,0x52,0xFF,0xEF,0xB8,0x09,0xC0,0xBA, +0x56,0xFF,0xEF,0xC7,0x06,0xAC,0x13,0x2C,0x01,0xC7,0x06,0xAE,0x13,0x04,0x00,0xC6, +0x06,0x91,0x12,0x00,0xC3,0x90,0x8A,0x1E,0x0B,0x01,0x2A,0xFF,0x6B,0xC3,0x05,0xD1, +0xE8,0xA3,0x18,0x01,0xC3,0x90,0x52,0xBA,0x50,0xFF,0xED,0x5A,0xC3,0x90,0x53,0x51, +0x8B,0x1E,0x18,0x01,0xB9,0x32,0x05,0x90,0xE2,0xFE,0x4B,0x75,0xF7,0x59,0x5B,0xC3, +0xB0,0x80,0xBA,0x00,0x01,0x0A,0x06,0x1A,0x01,0xEE,0xC3,0x90,0xB0,0x40,0xEB,0xF2, +0xB0,0xC0,0xEB,0xEE,0xB0,0x00,0xEB,0xEA,0xFA,0x60,0x06,0x1E,0x16,0x2B,0xDB,0x8E, +0xDB,0x2E,0xA1,0x9C,0x4C,0x2E,0xA3,0x74,0x4C,0xA0,0x93,0x12,0x98,0x8B,0xE8,0x89, +0x26,0x2D,0x7A,0x80,0x3E,0xCA,0x13,0x00,0x74,0x03,0xE9,0x51,0x42,0xE8,0xC0,0xFF, +0xE8,0xAB,0xFF,0xE8,0xA8,0xFF,0xB0,0x20,0xC6,0x06,0x90,0x12,0x00,0xFF,0x16,0x7C, +0x12,0x8B,0xFD,0x83,0xFF,0x0A,0x72,0x11,0xE8,0xB9,0xFF,0xE8,0x90,0xFF,0xE8,0xAB, +0xFF,0xE8,0x8A,0xFF,0x83,0xEF,0x0A,0xEB,0xEA,0x0B,0xFF,0x74,0x0F,0xE8,0xA4,0xFF, +0xE8,0x7B,0xFF,0xE8,0x9A,0xFF,0xE8,0x75,0xFF,0x4F,0x75,0xF1,0xE8,0x95,0xFF,0xE8, +0x6C,0xFF,0xEB,0xB9,0x8A,0x86,0xA5,0x00,0x24,0xFD,0xEE,0x88,0x86,0xA5,0x00,0xC3, +0x8A,0x86,0xA6,0x00,0x0C,0x02,0xEE,0xC3,0x8B,0x76,0x38,0xF7,0xC6,0x01,0x00,0x74, +0xEF,0x8B,0x4E,0x36,0x8B,0x46,0x2E,0x3B,0xC1,0x73,0x02,0x8B,0xC8,0x2B,0xC1,0x89, +0x46,0x2E,0x01,0x4E,0x34,0xC4,0x7E,0x04,0x26,0x01,0x0D,0x8B,0x7E,0x2C,0x83,0xEA, +0x04,0xF3,0x6C,0x8E,0xC1,0x89,0x7E,0x2C,0x3B,0x46,0x3C,0x72,0x12,0xF7,0xC6,0x20, +0x00,0x75,0x0B,0x83,0xCE,0x20,0x89,0x76,0x38,0xB0,0x00,0xE8,0xA0,0xFC,0xC3,0xF7, +0xC6,0x04,0x00,0x74,0x1B,0x8B,0xD8,0x83,0xCE,0x10,0x89,0x76,0x38,0x8A,0x86,0xA7, +0x00,0x24,0xFE,0x88,0x86,0xA7,0x00,0x83,0xC2,0x08,0xEE,0x83,0xEA,0x08,0x8B,0xC3, +0x3D,0x40,0x00,0x72,0x01,0xC3,0x81,0x4E,0x38,0x00,0x04,0x83,0xC2,0x02,0x8A,0x86, +0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5,0x00,0xEE,0xC3,0x8A,0x86,0xA6,0x00,0x0C,0x02, +0xEE,0xC3,0xF7,0x46,0x38,0x01,0x00,0x74,0xF1,0x8B,0x4E,0x2E,0x32,0xDB,0x8A,0xBE, +0xA3,0x00,0x83,0xC2,0x06,0xC4,0x76,0x04,0x8B,0x7E,0x2C,0x83,0xF9,0x08,0x72,0x2C, +0xEC,0xA8,0x01,0x74,0x16,0x8A,0xE0,0x83,0xEA,0x0A,0xEC,0x83,0xC2,0x0A,0x84,0xE7, +0x75,0x51,0xAA,0xFE,0xC3,0x49,0x83,0xF9,0x08,0x73,0xE5,0x32,0xFF,0x26,0x01,0x1C, +0x01,0x5E,0x34,0x89,0x76,0x04,0x89,0x4E,0x2E,0x89,0x7E,0x2C,0x3B,0x4E,0x3C,0x72, +0x11,0xF6,0x46,0x38,0x20,0x74,0x01,0xC3,0x83,0x4E,0x38,0x20,0xB0,0x00,0xE8,0xFD, +0xFB,0xC3,0xF6,0x46,0x38,0x04,0x74,0x15,0x83,0x4E,0x38,0x10,0x8A,0x86,0xA7,0x00, +0x24,0xFE,0x88,0x86,0xA7,0x00,0x83,0xEA,0x02,0xEE,0x83,0xC2,0x02,0x3D,0x40,0x00, +0x72,0x5D,0xC3,0x32,0xFF,0x26,0x03,0x1C,0x85,0xDB,0x74,0x09,0x26,0x89,0x1C,0x8B, +0xF7,0x47,0x47,0x49,0x49,0x80,0xE4,0x1E,0x80,0xCC,0xC0,0x26,0x89,0x04,0xF6,0xC4, +0x10,0x74,0x27,0x8B,0x76,0x38,0xF7,0xC6,0x00,0x10,0x74,0x0B,0x50,0xFE,0x86,0xB2, +0x00,0xB0,0x0A,0xE8,0xA8,0xFB,0x58,0xF7,0xC6,0x00,0x01,0x74,0x0D,0xE8,0x68,0x26, +0x8B,0x76,0x38,0x8B,0x4E,0x2E,0x8B,0x7E,0x04,0xAB,0x8B,0xF7,0x33,0xC0,0xAB,0x32, +0xDB,0x8A,0xBE,0xA3,0x00,0x49,0x49,0x83,0xF9,0x08,0x72,0x17,0xE9,0x41,0xFF,0x81, +0x4E,0x38,0x00,0x04,0x83,0xC2,0xF8,0x8A,0x86,0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5, +0x00,0xEE,0xC3,0xE9,0x45,0xFF,0x83,0xC2,0x08,0xEC,0x88,0x86,0xAA,0x00,0xC0,0xE8, +0x04,0x8A,0xE0,0x8A,0xC8,0x86,0x86,0xA9,0x00,0x32,0xE0,0x8B,0x5E,0x3E,0x84,0xE3, +0x74,0x4F,0x8A,0xC1,0x8B,0x4E,0x26,0xF6,0xC5,0x04,0x74,0x0C,0xA8,0x08,0x74,0x05, +0x80,0xE1,0xBF,0xEB,0x03,0x80,0xC9,0x40,0xF6,0xC5,0x08,0x74,0x0C,0xA8,0x02,0x74, +0x05,0x80,0xE1,0x7F,0xEB,0x03,0x80,0xC9,0x80,0x88,0x4E,0x26,0x8B,0xF0,0x8A,0x86, +0xA5,0x00,0x84,0xC9,0x74,0x08,0xA8,0x02,0x74,0x15,0x24,0xFD,0xEB,0x06,0xA8,0x02, +0x75,0x0D,0x0C,0x02,0x88,0x86,0xA5,0x00,0x83,0xEA,0x0A,0xEE,0x83,0xC2,0x0A,0x8B, +0xC6,0x84,0xE7,0x75,0x01,0xC3,0xC6,0x86,0xBA,0x00,0x01,0xB0,0x0E,0xE8,0xEE,0xFA, +0xF7,0x46,0x38,0x00,0x02,0x74,0xEE,0x83,0x7E,0x2E,0x06,0x72,0xE8,0x8A,0xA6,0xAA, +0x00,0xC4,0x5E,0x04,0x8B,0x7E,0x2C,0xB0,0xFF,0xAA,0xB0,0x02,0xAB,0x26,0x83,0x07, +0x03,0x83,0x6E,0x2E,0x03,0x89,0x7E,0x2C,0xF6,0x46,0x38,0x20,0x74,0x01,0xC3,0x83, +0x4E,0x38,0x20,0xB0,0x00,0xE8,0xB6,0xFA,0xC3,0x90,0x83,0xEA,0x08,0xE9,0xB4,0xFD, +0x83,0xC2,0x06,0x8B,0x5E,0x26,0xF6,0xC3,0xC0,0x75,0xEF,0x8B,0x4E,0x1C,0xEC,0x88, +0x86,0xA4,0x00,0x83,0xEA,0x0A,0xA8,0x20,0x75,0x02,0x8A,0xCD,0x32,0xED,0x8B,0x46, +0x1A,0x3B,0xC8,0x73,0x18,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A,0xC5,0x76,0x00, +0xF3,0x6E,0x8E,0xD9,0x89,0x76,0x00,0x3D,0x20,0x00,0x72,0x30,0xC3,0x85,0xC0,0x74, +0x31,0x8B,0xC8,0x01,0x46,0x2A,0xC5,0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x80,0xCB,0x02, +0x89,0x5E,0x26,0xE8,0x32,0xF1,0xF6,0xC7,0x01,0x75,0x16,0x83,0xC2,0x02,0xE8,0x53, +0xFD,0xF6,0xC7,0x10,0x75,0x0B,0xB0,0x02,0xE8,0x43,0xFA,0xC3,0xF6,0xC7,0x01,0x74, +0xF0,0xC3,0x80,0xCB,0x02,0x89,0x5E,0x26,0xF6,0xC7,0x01,0x74,0xDE,0x83,0xC2,0x02, +0xE8,0x31,0xFD,0xF6,0x86,0xA4,0x00,0x40,0x74,0x0B,0x80,0xE7,0xFE,0x80,0xCF,0x02, +0x89,0x5E,0x26,0xEB,0xCC,0xB0,0x04,0xE8,0x14,0xFA,0xC3,0xC0,0xC2,0xC8,0xCA,0xC4, +0xC6,0xCC,0xCE,0xD0,0xD2,0xD8,0xDA,0xD4,0xD6,0xDC,0xDE,0x90,0xE9,0x0E,0x01,0xE4, +0xC4,0x8A,0xE0,0xE4,0xC4,0x8B,0xD0,0x83,0xF9,0x08,0x72,0xF0,0x26,0x83,0x3F,0x00, +0x74,0x04,0x8B,0xDF,0x49,0x49,0x8B,0xFB,0x8A,0xDE,0x83,0xE3,0x0F,0x2E,0x8A,0xA7, +0x2B,0x16,0xAB,0xF6,0xC4,0x10,0x74,0x24,0xF7,0xC6,0x00,0x10,0x74,0x0B,0x50,0xFE, +0x86,0xB2,0x00,0xB0,0x0A,0xE8,0xC6,0xF9,0x58,0xF7,0xC6,0x00,0x01,0x74,0x0D,0xE8, +0x86,0x24,0x8B,0x76,0x38,0x8B,0x4E,0x2E,0x8B,0x7E,0x04,0xAB,0x89,0x7E,0x04,0x33, +0xC0,0xAB,0x49,0x49,0x89,0x4E,0x2E,0x89,0x7E,0x2C,0x8B,0xC1,0xEB,0x4E,0x90,0xEB, +0x9E,0x90,0xE4,0xD6,0x84,0xC0,0x79,0x63,0xE6,0xD0,0x8A,0xC8,0x25,0x03,0x00,0x03, +0xD8,0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0x88,0x8E,0xAE,0x00,0x8B,0x4E,0x2E,0xC4, +0x5E,0x04,0x8B,0x7E,0x2C,0x8B,0x76,0x38,0xE4,0x86,0x24,0x07,0x3C,0x03,0x75,0xCF, +0xE4,0x1C,0x91,0x3B,0xC1,0x73,0x02,0x8B,0xC8,0x2B,0xC1,0x89,0x46,0x2E,0x01,0x4E, +0x34,0x26,0x01,0x0F,0xBA,0xC4,0x00,0xF3,0x6C,0x89,0x7E,0x2C,0x3B,0x46,0x3C,0x72, +0x1C,0xF7,0xC6,0x20,0x00,0x75,0x0B,0x83,0xCE,0x20,0x89,0x76,0x38,0xB0,0x00,0xE8, +0x3C,0xF9,0x8A,0x86,0xAE,0x00,0x24,0x3F,0xE6,0xD6,0xC3,0xF9,0xC3,0xF7,0xC6,0x0A, +0x00,0x74,0x35,0xF7,0xC6,0x10,0x00,0x75,0x2F,0x83,0xCE,0x10,0x89,0x76,0x38,0xF7, +0xC6,0x02,0x00,0x74,0x0E,0x50,0xE4,0xD8,0x24,0xFE,0xE6,0xD8,0x58,0xF7,0xC6,0x08, +0x00,0x74,0x15,0x50,0x51,0xB9,0xE8,0x03,0xE4,0x0A,0x84,0xC0,0xE0,0xFA,0x84,0xC0, +0x75,0x04,0xB0,0x24,0xE6,0x0A,0x59,0x58,0x3D,0x40,0x00,0x73,0xB5,0x8A,0x86,0xA5, +0x00,0x24,0xEF,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x81,0xCE,0x10,0x04,0x89,0x76,0x38, +0xEB,0xA0,0x00,0x08,0x04,0x0C,0x01,0x09,0x05,0x0D,0x02,0x0A,0x06,0x0E,0x03,0x0B, +0x07,0x0F,0x00,0x40,0x80,0xC0,0x20,0x60,0xA0,0xE0,0x10,0x50,0x90,0xD0,0x30,0x70, +0xB0,0xF0,0xE4,0xD2,0xE6,0xD0,0x8A,0xC8,0x25,0x03,0x00,0x03,0xD8,0xD1,0xE3,0x2E, +0x8B,0xAF,0x44,0x00,0x88,0x8E,0xAE,0x00,0xE4,0xD8,0xC0,0xE8,0x04,0x8B,0xD8,0x2E, +0x8A,0x87,0x62,0x17,0x8A,0xE0,0x8A,0xC8,0x86,0x86,0xA9,0x00,0x32,0xE0,0xE4,0x98, +0x8B,0x5E,0x3E,0x84,0xE3,0x74,0x54,0x8A,0xC1,0x8B,0x4E,0x26,0xF6,0xC5,0x04,0x74, +0x0C,0xA8,0x08,0x74,0x05,0x80,0xE1,0xBF,0xEB,0x03,0x80,0xC9,0x40,0xF6,0xC5,0x08, +0x74,0x0C,0xA8,0x02,0x74,0x05,0x80,0xE1,0x7F,0xEB,0x03,0x80,0xC9,0x80,0x88,0x4E, +0x26,0x8B,0xF0,0x8A,0x86,0xA5,0x00,0xF6,0xC1,0xFD,0x74,0x08,0xA8,0x06,0x74,0x19, +0x24,0xF9,0xEB,0x0F,0xA8,0x06,0x75,0x11,0xF6,0xC5,0x01,0x75,0x04,0x0C,0x04,0xEB, +0x02,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x8B,0xC6,0x84,0xE7,0x75,0x09,0x8A, +0x86,0xAE,0x00,0x24,0x3F,0xE6,0xD2,0xC3,0xC6,0x86,0xBA,0x00,0x01,0xB0,0x0E,0xE8, +0x1C,0xF8,0xF7,0x46,0x38,0x00,0x02,0x74,0xE6,0x83,0x7E,0x2E,0x06,0x72,0xE0,0x8A, +0x86,0xA9,0x00,0x8A,0xE0,0x86,0x86,0xAA,0x00,0x8A,0xC8,0x32,0xC4,0x80,0xC9,0x0B, +0x22,0xC1,0xC0,0xE4,0x04,0x0A,0xE0,0xC4,0x5E,0x04,0x8B,0x7E,0x2C,0xB0,0xFF,0xAA, +0xB0,0x02,0xAB,0x26,0x83,0x07,0x03,0x83,0x6E,0x2E,0x03,0x89,0x7E,0x2C,0xF6,0x46, +0x38,0x20,0x75,0xAB,0x83,0x4E,0x38,0x20,0xB0,0x00,0xE8,0xD1,0xF7,0xEB,0xA0,0x90, +0xE4,0x12,0x24,0xDF,0xE6,0x12,0x81,0xE3,0xFE,0x9F,0x89,0x5E,0x26,0x83,0x66,0x48, +0xF7,0xEB,0x73,0x90,0xF6,0xC7,0x20,0x75,0xE7,0xE4,0x12,0x0C,0x20,0xE6,0x12,0x32, +0xC0,0xE6,0xC6,0xB0,0x83,0xE6,0xC6,0x80,0xCF,0x20,0x89,0x5E,0x26,0x8A,0x86,0xA5, +0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xEB,0x74,0x90,0xF6,0xC7,0x40,0x75, +0xD3,0xE4,0x12,0x0C,0x20,0xE6,0x12,0x32,0xC0,0xE6,0xC6,0xB0,0x81,0xE6,0xC6,0x80, +0xE7,0xDF,0x80,0xCB,0x01,0x89,0x5E,0x26,0xB0,0x06,0xE8,0x71,0xF7,0x90,0x8A,0x86, +0xA5,0x00,0x24,0xF9,0xE6,0x0C,0x88,0x86,0xA5,0x00,0xEB,0x43,0xE4,0xD4,0xE6,0xD0, +0x8B,0xF8,0x25,0x03,0x00,0x03,0xD8,0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0x8B,0x5E, +0x26,0xF6,0xC7,0x60,0x75,0xB6,0xF6,0xC3,0xC0,0x75,0xD3,0xBA,0xC6,0x00,0x8B,0x4E, +0x1C,0x8B,0x46,0x1A,0x3B,0xC8,0x73,0x1E,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A, +0xC5,0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x89,0x76,0x00,0x3D,0x20,0x00,0x72,0x3D,0x8B, +0xC7,0x24,0x3F,0xE6,0xD4,0xC3,0x85,0xC0,0x74,0x39,0x8B,0xC8,0x01,0x46,0x2A,0xC5, +0x76,0x00,0xF3,0x6E,0x8E,0xD9,0x83,0xCB,0x02,0x89,0x5E,0x26,0xE8,0xD9,0xED,0xF6, +0xC7,0x01,0x75,0x39,0x8A,0x86,0xA5,0x00,0x24,0xF9,0xE6,0x0C,0x88,0x86,0xA5,0x00, +0xF6,0xC7,0x10,0x75,0xCA,0xB0,0x02,0xE8,0xE4,0xF6,0xEB,0xC3,0xF6,0xC7,0x01,0x74, +0xEF,0xEB,0xBC,0xF6,0xC7,0x01,0x74,0xDC,0x8A,0x86,0xA5,0x00,0xA8,0x02,0x74,0x11, +0x81,0xE3,0xFF,0xFE,0x81,0xCB,0x00,0x02,0x89,0x5E,0x26,0xEB,0xC7,0x8A,0x86,0xA5, +0x00,0x24,0xFB,0x0C,0x02,0xE6,0x0C,0x88,0x86,0xA5,0x00,0xEB,0x92,0x90,0xFD,0xF7, +0xDF,0x7F,0xFE,0xFB,0xEF,0xBF,0x00,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x02,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x03,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x02,0x04,0x00,0x04,0x05,0x04,0x05,0x04,0x01,0x04, +0x00,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x07,0x04,0x07,0x04,0x05,0x04,0x05,0x04,0x07,0x04, +0x07,0x04,0x05,0x04,0x05,0x04,0x06,0x04,0x06,0x04,0x05,0x04,0x05,0x04,0x06,0x04, +0x06,0x04,0x05,0x04,0x05,0x04,0x33,0xDB,0x8A,0xD8,0x8A,0x87,0x6C,0x12,0xE6,0xFE, +0xC1,0xE3,0x02,0xE4,0xCE,0xA8,0x04,0x75,0x09,0xA8,0x02,0x74,0x03,0xE9,0x2C,0xFE, +0xF9,0xC3,0x50,0x53,0xE8,0xCB,0xFC,0x5B,0x58,0xA8,0x02,0x74,0x03,0xE9,0x1C,0xFE, +0xF8,0xC3,0x33,0xDB,0x8A,0xD8,0x8A,0x87,0x6C,0x12,0xE6,0xFE,0xC1,0xE3,0x02,0xE9, +0xD0,0xFB,0x96,0x1A,0xC2,0x1A,0x00,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0C,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0E,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0C,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x0A,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0x08,0x00,0x02,0x00,0x04,0x00,0x02,0x00,0x06,0x00, +0x02,0x00,0x04,0x00,0x02,0x00,0xC3,0x90,0xD6,0x14,0x90,0x15,0x58,0x13,0xE2,0x13, +0xD6,0x1B,0xD6,0x1B,0xE2,0x13,0xD6,0x1B,0x8B,0x94,0x64,0x12,0xC1,0xE6,0x04,0xA8, +0x01,0x74,0x35,0x50,0x33,0xC0,0x8A,0xC2,0xE6,0xFE,0xE4,0xA0,0x85,0xC0,0x74,0x27, +0x8B,0xD8,0x2E,0x8A,0x9F,0xD6,0x1A,0x52,0x56,0x2E,0x8B,0xA8,0x44,0x00,0x8B,0x56, +0x28,0xEC,0xA8,0x01,0x75,0x0D,0x88,0x86,0xAD,0x00,0x24,0x0E,0x8A,0xD8,0x2E,0xFF, +0x97,0xD8,0x1B,0x5E,0x5A,0xEB,0xCD,0x58,0xA8,0x02,0x74,0x36,0x83,0xC6,0x10,0x33, +0xC0,0x8A,0xC6,0xE6,0xFE,0xE4,0xA0,0x85,0xC0,0x74,0x27,0x8B,0xD8,0x2E,0x8A,0x9F, +0xD6,0x1A,0x52,0x56,0x2E,0x8B,0xA8,0x44,0x00,0x8B,0x56,0x28,0xEC,0xA8,0x01,0x75, +0x0D,0x88,0x86,0xAD,0x00,0x24,0x0E,0x8A,0xD8,0x2E,0xFF,0x97,0xD8,0x1B,0x5E,0x5A, +0xEB,0xCD,0xC3,0x90,0x32,0xE4,0x8B,0xD8,0x8B,0xD0,0x2E,0x8A,0x9F,0x96,0x19,0x2E, +0x22,0x97,0x8E,0x19,0x56,0x52,0x8A,0xC3,0x24,0x03,0x03,0xC6,0x80,0xE3,0x04,0xD0, +0xEB,0x2E,0xFF,0x97,0xD2,0x1A,0x58,0x5E,0xA9,0x55,0x00,0x75,0xD9,0xC3,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x08, +0x33,0xF6,0xE8,0xBF,0xFF,0xEB,0xEE,0x90,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1, +0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x08,0xBE,0x04,0x00,0xE8,0x94,0xFF, +0xEB,0xED,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61, +0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x6B,0xFF,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x74,0xE5,0xBE,0x08,0x00,0xE8,0x5A,0xFF,0xEB,0xDD,0xA1,0x60,0x12,0xE6, +0xFE,0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0xA1,0x5C,0x12,0xE6, +0xFE,0xE4,0x04,0x1F,0xE4,0x04,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4, +0x74,0x19,0xBE,0x04,0x00,0xE8,0x1C,0xFF,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00,0x22, +0xC4,0x74,0xE4,0xBE,0x0C,0x00,0xE8,0x0B,0xFF,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE, +0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x04,0x1F,0xE4,0x04,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x08, +0x33,0xF6,0xE8,0x53,0xFE,0xEB,0xEE,0x90,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07, +0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x08,0xBE,0x02,0x00,0xE8,0x2C,0xFE,0xEB,0xED,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x08,0xBE,0x04,0x00,0xE8, +0x06,0xFE,0xEB,0xED,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4, +0x74,0x08,0xBE,0x06,0x00,0xE8,0xE0,0xFD,0xEB,0xED,0xB8,0x00,0x80,0xBA,0x22,0xFF, +0xEF,0x07,0x1F,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12, +0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x37,0xFE,0xA1,0x60,0x12, +0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0xE5,0xBE,0x04,0x00,0xE8,0xAA,0xFD,0xEB,0xDD, +0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0xA1,0x5C,0x12,0xE6,0xFE, +0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x90, +0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4, +0x74,0x19,0xBE,0x04,0x00,0xE8,0xEC,0xFD,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84, +0xC4,0x74,0xE4,0xBE,0x06,0x00,0xE8,0x5F,0xFD,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x75,0xED,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x04,0x07,0xE4,0x04, +0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0xA1,0x5C,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x27, +0xFD,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74,0xE5,0xBE,0x08,0x00,0xE8, +0x92,0xFD,0xEB,0xDD,0xA1,0x60,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x75,0xED,0xE4, +0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x61,0xCF,0x60,0x1E, +0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x74,0x19, +0xBE,0x02,0x00,0xE8,0xE2,0xFC,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00,0x22,0xC4,0x74, +0xE4,0xBE,0x0C,0x00,0xE8,0x4D,0xFD,0xEB,0xDC,0xA1,0x62,0x12,0xE6,0xFE,0xE4,0x00, +0x22,0xC4,0x75,0xED,0xE4,0x04,0x07,0xE4,0x04,0x1F,0xB8,0x00,0x80,0xBA,0x22,0xFF, +0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5C,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x18,0x33,0xF6,0xE8,0x9D,0xFC,0xA1,0x60,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0xE5,0xBE,0x04,0x00,0xE8,0x8C,0xFC,0xEB,0xDD,0xA1,0x60, +0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0x07,0x1F,0xB8,0x00,0x80,0xBA,0x22, +0xFF,0xEF,0x61,0xCF,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0xA1,0x5E,0x12,0xE6,0xFE, +0xE4,0x80,0x84,0xC4,0x74,0x19,0xBE,0x02,0x00,0xE8,0x5C,0xFC,0xA1,0x62,0x12,0xE6, +0xFE,0xE4,0x80,0x84,0xC4,0x74,0xE4,0xBE,0x06,0x00,0xE8,0x4B,0xFC,0xEB,0xDC,0xA1, +0x62,0x12,0xE6,0xFE,0xE4,0x80,0x84,0xC4,0x75,0xED,0x07,0x1F,0xB8,0x00,0x80,0xBA, +0x22,0xFF,0xEF,0x61,0xCF,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0x90,0x2A,0xC0, +0xE6,0xFE,0xE4,0xCE,0xA8,0x01,0x74,0x14,0x33,0xDB,0xE8,0xD5,0xF6,0xEB,0xEF,0x90, +0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xF6,0x06,0x05,0x01, +0x01,0x75,0xED,0xB0,0x01,0xE6,0xFE,0xE4,0xCE,0xA8,0x01,0x74,0xE3,0xBB,0x04,0x00, +0xE8,0xAF,0xF6,0xEB,0xC9,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E,0xD8,0x90,0xFB,0x90, +0xFA,0x2A,0xC0,0xE6,0xFE,0xE4,0xCE,0xA8,0x02,0x74,0x13,0x33,0xDB,0xE8,0xCC,0xF8, +0xEB,0xEC,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xA8,0x04, +0x74,0xF0,0x33,0xDB,0xE8,0x5B,0xF7,0xEB,0xD5,0x90,0x60,0x1E,0x06,0x2B,0xC0,0x8E, +0xD8,0x90,0xFB,0x90,0xFA,0xB0,0x01,0xE6,0xFE,0xE4,0xCE,0xA8,0x02,0x74,0x15,0xBB, +0x04,0x00,0xE8,0x97,0xF8,0xEB,0xEB,0x90,0xB8,0x00,0x80,0xBA,0x22,0xFF,0xEF,0x07, +0x1F,0x61,0xCF,0x90,0xA8,0x04,0x74,0xF0,0xBB,0x04,0x00,0xE8,0x24,0xF7,0xEB,0xD2, +0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x09,0x9C,0x0E,0xE8,0x6B,0xF2,0x90,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x29,0x9C,0x0E,0xE8,0x5D,0xF2,0x90,0x6E,0x20,0x6E,0x20, +0x6E,0x20,0xCA,0x1D,0x8E,0x1C,0xE2,0x1C,0x16,0x1E,0x6E,0x20,0x7E,0x1D,0xAA,0x1E, +0x34,0x1F,0x6E,0x20,0x7E,0x1D,0x6E,0x20,0x6E,0x20,0x34,0x1F,0x6E,0x20,0x6E,0x20, +0x6E,0x20,0xF0,0x1D,0xB8,0x1C,0x30,0x1D,0x60,0x1E,0x6E,0x20,0xA4,0x1D,0xEE,0x1E, +0x74,0x1F,0x6E,0x20,0xA4,0x1D,0x6E,0x20,0x6E,0x20,0x74,0x1F,0xFC,0xB9,0x40,0x00, +0x8C,0xCB,0xB8,0x60,0x20,0x2B,0xFF,0xAB,0x93,0xAB,0x93,0xE2,0xFA,0xC7,0x06,0x4C, +0x00,0xA4,0x11,0x83,0x3E,0x44,0x12,0x00,0x75,0x20,0xC7,0x06,0x3C,0x00,0xEA,0x4A, +0xC7,0x06,0x30,0x00,0xB6,0x1F,0xC7,0x06,0x34,0x00,0xF6,0x1F,0xF6,0x06,0x05,0x01, +0x01,0x75,0x06,0xC7,0x06,0x38,0x00,0x2A,0x20,0xC3,0xC7,0x06,0x3C,0x00,0x38,0x4B, +0x33,0xDB,0x8A,0x1E,0x54,0x12,0xC1,0xE3,0x02,0x02,0x1E,0x56,0x12,0x2E,0x8B,0x87, +0x7C,0x20,0xA3,0x30,0x00,0x8A,0x1E,0x55,0x12,0xC1,0xE3,0x02,0x02,0x1E,0x57,0x12, +0x2E,0x8B,0x87,0x9C,0x20,0xA3,0x34,0x00,0xC3,0x8B,0x86,0x9E,0x00,0xE6,0xFE,0x86, +0xC4,0xE6,0xD0,0xC3,0x8B,0x86,0x9E,0x00,0xE6,0xFE,0x33,0xD2,0x8A,0xD4,0xC3,0x51, +0xB9,0x10,0x27,0xE4,0x0A,0x90,0x90,0x84,0xC0,0x74,0x05,0xE2,0xF6,0x59,0xF9,0xC3, +0x59,0xF8,0xC3,0x84,0xC0,0x78,0x1E,0x51,0x8A,0xE8,0x8A,0xC8,0xB8,0x01,0x00,0xD3, +0xE0,0x09,0x86,0x98,0x00,0x3A,0xAE,0xA0,0x00,0x59,0x75,0x10,0xE8,0xA9,0xE5,0x83, +0x4E,0x26,0x02,0xF9,0xC3,0x98,0x89,0x86,0x98,0x00,0xEB,0xF0,0xF8,0xC3,0x84,0xC0, +0x78,0x12,0x51,0x8A,0xE0,0x8A,0xC8,0xB8,0x01,0x00,0xD3,0xE0,0x59,0xF7,0xD0,0x21, +0x86,0x98,0x00,0xC3,0xC7,0x86,0x98,0x00,0x00,0x00,0xC3,0x83,0xC2,0x04,0x8A,0x86, +0xA6,0x00,0x0C,0x04,0xEE,0x83,0xEA,0x04,0xC3,0xE8,0x93,0xFF,0x72,0x04,0xB0,0x82, +0xE6,0x0A,0xC3,0x8B,0x46,0x26,0xA8,0xFD,0x74,0x11,0x8A,0x86,0xA5,0x00,0xA8,0x06, +0x74,0x08,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xF6,0xC4,0x01,0x74,0x0A, +0x8A,0x86,0xA5,0x00,0x24,0xFB,0x0C,0x02,0xEB,0x0C,0xA8,0x02,0x75,0x0F,0x8A,0x86, +0xA5,0x00,0x24,0xFD,0x0C,0x04,0x3A,0x86,0xA5,0x00,0x75,0xD8,0xC3,0x8A,0x86,0xA5, +0x00,0xEB,0xCF,0xE4,0xD8,0x33,0xDB,0x8A,0xD8,0xC0,0xEB,0x04,0x2E,0x8A,0x9F,0x62, +0x17,0x88,0x9E,0xA9,0x00,0x8B,0x5E,0x26,0x80,0xE3,0x3F,0xF6,0xC7,0x04,0x74,0x07, +0xA8,0x10,0x75,0x03,0x80,0xCB,0x40,0xF6,0xC7,0x08,0x74,0x07,0xA8,0x80,0x75,0x03, +0x80,0xCB,0x40,0x88,0x5E,0x26,0x8A,0x86,0xA5,0x00,0xF6,0xC3,0xFD,0x74,0x0D,0xA8, +0x06,0x74,0x08,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xF6,0xC7,0x01,0x74, +0x04,0x0C,0x02,0xEB,0xF0,0xF6,0xC3,0x02,0x75,0xE9,0x0C,0x04,0xEB,0xE7,0xC4,0x04, +0xC4,0x04,0x85,0x04,0x59,0x04,0x48,0x04,0x41,0x04,0xC3,0x03,0x82,0x03,0x41,0x03, +0x82,0x02,0x57,0x02,0x41,0x02,0x82,0x01,0x41,0x01,0x82,0x00,0x41,0x00,0x4E,0x02, +0xAD,0x01,0x57,0x01,0x2D,0x00,0x2B,0x00,0x27,0x00,0x21,0x00,0x16,0x00,0xF4,0x04, +0xF4,0x04,0xA3,0x04,0x6F,0x04,0x5B,0x04,0x51,0x04,0xF4,0x03,0xA3,0x03,0x51,0x03, +0xA3,0x02,0x6D,0x02,0x51,0x02,0xA3,0x01,0x51,0x01,0xA3,0x00,0x51,0x00,0x62,0x02, +0xD9,0x01,0x6D,0x01,0x38,0x00,0x36,0x00,0x31,0x00,0x29,0x00,0x1B,0x00,0x51,0x57, +0xBF,0x02,0x00,0xEB,0x0F,0x90,0x51,0x56,0xBF,0x01,0x00,0xEB,0x07,0x90,0x51,0x56, +0xBF,0x03,0x00,0x90,0x3C,0x19,0x76,0x02,0xB0,0x17,0x98,0x8B,0xF0,0x8A,0x82,0xC4, +0x00,0x2A,0xE4,0x8B,0xF0,0x83,0xFE,0x18,0x73,0x46,0xD1,0xE6,0x2E,0x8B,0x8C,0x4E, +0x22,0xF7,0x46,0x38,0x80,0x00,0x74,0x05,0x2E,0x8B,0x8C,0x7E,0x22,0xF7,0xC7,0x02, +0x00,0x74,0x12,0x3B,0x8E,0x94,0x00,0x74,0x0C,0x89,0x8E,0x94,0x00,0x8A,0xC5,0xE6, +0xEC,0x8A,0xC1,0xE6,0xE4,0xF7,0xC7,0x01,0x00,0x74,0x12,0x3B,0x8E,0x96,0x00,0x74, +0x0C,0x89,0x8E,0x96,0x00,0x8A,0xC5,0xE6,0xF8,0x8A,0xC1,0xE6,0xF0,0x5E,0x59,0xC3, +0x77,0x06,0x8B,0x8E,0x8E,0x00,0xEB,0xC5,0x8B,0x8E,0x90,0x00,0xEB,0xBF,0xD5,0x03, +0xF6,0x00,0x3E,0x00,0x10,0x00,0x04,0x00,0xCA,0x04,0x33,0x01,0x4D,0x00,0x14,0x00, +0x05,0x00,0x01,0x03,0x05,0x07,0x09,0x00,0x01,0x02,0x03,0x04,0x80,0x84,0x1E,0x00, +0xA0,0x25,0x26,0x00,0x00,0x00,0x60,0x8B,0xF0,0x33,0xFF,0x2E,0xA1,0x4C,0x23,0x2E, +0x8B,0x16,0x4E,0x23,0xBB,0x2E,0x23,0xF7,0x46,0x38,0x80,0x00,0x74,0x0C,0x2E,0xA1, +0x50,0x23,0x2E,0x8B,0x16,0x52,0x23,0xBB,0x38,0x23,0xB9,0x05,0x00,0x2E,0x3B,0x31, +0x73,0x0A,0x47,0x47,0xE2,0xF7,0xB8,0xFF,0xFF,0xEB,0x1D,0x90,0xD1,0xEF,0x2E,0x8A, +0x8D,0x42,0x23,0x2A,0xED,0xD1,0xEA,0xD1,0xD8,0xE2,0xFA,0xF7,0xF6,0x05,0x02,0x00, +0xC1,0xE8,0x02,0x2E,0x8A,0xA5,0x47,0x23,0x2E,0xA3,0x54,0x23,0x61,0x2E,0xA1,0x54, +0x23,0xC3,0x08,0x00,0x20,0x00,0x80,0x00,0x00,0x02,0x60,0x09,0x08,0x00,0x20,0x00, +0x80,0x00,0x00,0x02,0x00,0x08,0x00,0x00,0x01,0x00,0x02,0x00,0x03,0x00,0x04,0x00, +0x52,0x56,0x57,0x85,0xC0,0x74,0x05,0x3D,0x01,0x09,0x76,0x03,0xB8,0x01,0x09,0xBF, +0x5B,0x01,0xF7,0x46,0x38,0x80,0x00,0x74,0x03,0xBF,0xB2,0x01,0x33,0xF6,0x2E,0x3B, +0x84,0xB2,0x23,0x76,0x04,0x46,0x46,0xEB,0xF5,0xF7,0xE7,0x2E,0x8B,0xBC,0xBC,0x23, +0x03,0xC7,0x83,0xD2,0x00,0xD1,0xE7,0xF7,0xF7,0x2E,0x8A,0xA4,0xC6,0x23,0x5F,0x5E, +0x5A,0xC3,0xE4,0x3E,0x80,0xBE,0xC3,0x00,0x03,0x75,0x0C,0xF7,0x46,0x7A,0x20,0x00, +0x74,0x05,0x0C,0x80,0xE6,0x3E,0xC3,0x24,0x7F,0xE6,0x3E,0xC3,0x24,0x03,0x88,0x86, +0xC3,0x00,0x8A,0xE0,0xE4,0x10,0x24,0xFC,0x0A,0xC4,0xE6,0x10,0x80,0x8E,0xA1,0x00, +0x42,0xE8,0xCE,0xFF,0xC3,0x90,0x56,0x8B,0xF0,0x83,0xE6,0x07,0xD1,0xE6,0x2E,0xFF, +0xA4,0x54,0x24,0x90,0x64,0x24,0x68,0x24,0x6C,0x24,0x70,0x24,0x74,0x24,0x83,0x24, +0x83,0x24,0x83,0x24,0xB4,0x00,0xEB,0x0E,0xB4,0xC0,0xEB,0x0A,0xB4,0x40,0xEB,0x06, +0xB4,0x20,0xEB,0x02,0xB4,0xA0,0xE4,0x10,0x24,0x1F,0x0A,0xC4,0xE6,0x10,0x80,0x8E, +0xA1,0x00,0x42,0x5E,0xC3,0x90,0x3C,0x02,0x77,0x12,0x8A,0xE0,0xE4,0x10,0x24,0xF3, +0xC0,0xE4,0x02,0x0A,0xC4,0xE6,0x10,0x80,0x8E,0xA1,0x00,0x42,0xC3,0x90,0x8B,0x5E, +0x38,0x84,0xC0,0x74,0x1F,0x3C,0x02,0x74,0x20,0x83,0xCB,0x08,0x8B,0x46,0x2E,0x3B, +0x46,0x3C,0x77,0x0C,0xE8,0x88,0xFC,0x72,0x07,0xB0,0x24,0xE6,0x0A,0x83,0xCB,0x10, +0x89,0x5E,0x38,0xC3,0x83,0xE3,0xF7,0xEB,0xF7,0xF7,0xC3,0x10,0x00,0x74,0xF5,0xE8, +0x6D,0xFC,0x72,0xEC,0x8A,0x86,0xC0,0x00,0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xEB,0xE0, +0x8B,0x5E,0x38,0x8B,0x46,0x2E,0x3B,0x46,0x3C,0xE4,0xD8,0x77,0x0B,0x24,0xFE,0x80, +0xCB,0x12,0xE6,0xD8,0x89,0x5E,0x38,0xC3,0x0C,0x01,0x80,0xCB,0x02,0xEB,0xF3,0x50, +0x33,0xDB,0xC1,0xE8,0x04,0x25,0x0F,0x0F,0x8A,0xD8,0x2E,0x8A,0x87,0x62,0x17,0x8A, +0xDC,0x2E,0x8A,0xA7,0x62,0x17,0x09,0x46,0x3E,0x58,0xC3,0x50,0x33,0xDB,0xC1,0xE8, +0x04,0x25,0x0F,0x0F,0x8A,0xD8,0x2E,0x8A,0x87,0x62,0x17,0x8A,0xDC,0x2E,0x8A,0xA7, +0x62,0x17,0xF7,0xD0,0x21,0x46,0x3E,0x58,0xC3,0x8B,0x46,0x3E,0x33,0xDB,0x8A,0xD8, +0x0A,0xDC,0x2E,0x8A,0x87,0x72,0x17,0xE6,0x2C,0x8A,0xE0,0xE4,0x2A,0x24,0x0F,0x0A, +0xC4,0xE6,0x2A,0x8A,0x86,0xA5,0x00,0x84,0xE4,0x75,0x0D,0xA8,0x80,0x74,0x11,0x24, +0x7F,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xA8,0x80,0x75,0x04,0x0C,0x80,0xEB,0xF1, +0xC3,0x1E,0x60,0x33,0xC9,0x33,0xD2,0x33,0xF6,0x8E,0xD9,0x8D,0xBE,0xFD,0x00,0x57, +0x8B,0x05,0x84,0xC0,0x74,0x16,0x8B,0xD1,0x42,0x8B,0xFE,0x4F,0x78,0x09,0x38,0xA3, +0xE4,0x00,0x74,0x08,0x4F,0x79,0xF7,0x88,0xA2,0xE4,0x00,0x46,0x5F,0x83,0xC7,0x09, +0x41,0x83,0xF9,0x10,0x72,0xD9,0x89,0xB6,0x86,0x00,0x89,0x96,0x84,0x00,0x61,0x1F, +0xC3,0x53,0xC7,0x46,0x66,0x00,0x00,0x8B,0x46,0x64,0xA9,0x40,0x00,0x74,0x0D,0xB3, +0x00,0xA9,0x80,0x00,0x74,0x02,0xB3,0x7F,0x88,0x9E,0xC1,0x00,0x32,0xDB,0xA9,0x02, +0x00,0x74,0x03,0x80,0xCB,0x40,0xA9,0x00,0x40,0x74,0x03,0x80,0xCB,0x02,0xA9,0x00, +0x80,0x74,0x03,0x80,0xCB,0x01,0xA9,0x30,0x1E,0x74,0x03,0x80,0xCB,0xBC,0xA9,0x00, +0x20,0x74,0x03,0x80,0xCB,0x08,0xA9,0x04,0x01,0x74,0x03,0x80,0xCB,0x10,0xA9,0x08, +0x00,0x74,0x03,0x80,0xCB,0x20,0x88,0x9E,0xC2,0x00,0x5B,0xC3,0x06,0x51,0x57,0x50, +0x16,0x07,0x8D,0xBE,0xC4,0x00,0xB9,0x1F,0x00,0x33,0xC0,0xAA,0x40,0xE2,0xFC,0x8B, +0x86,0x92,0x00,0x89,0x86,0x8E,0x00,0x89,0x86,0x90,0x00,0x58,0x5F,0x59,0x07,0xC3, +0xE4,0xD8,0xC0,0xE8,0x04,0x53,0x25,0x0F,0x00,0x8B,0xD8,0x2E,0x8A,0x87,0x62,0x17, +0x88,0x86,0xA9,0x00,0x5A,0xC3,0x08,0x86,0xAC,0x00,0xC6,0x86,0xBA,0x00,0x01,0xB0, +0x0E,0xE8,0xEA,0xE9,0xC3,0xAD,0x36,0xA3,0xB4,0x13,0xAD,0x36,0xA3,0xB6,0x13,0xAD, +0x36,0xA3,0xB8,0x13,0x83,0xE9,0x06,0x36,0xF7,0x06,0xB6,0x13,0x0F,0x00,0xC3,0x8A, +0x46,0x26,0xF7,0x46,0x48,0x80,0x00,0x74,0x02,0x0C,0x10,0x88,0x86,0xBD,0x00,0x32, +0xC0,0x83,0x7E,0x1A,0x00,0x75,0x0E,0x8B,0x5E,0x40,0x43,0x80,0xE3,0xFE,0x3B,0x5E, +0x08,0x75,0x02,0x0C,0x01,0x83,0x7E,0x3A,0x00,0x75,0x0D,0x1E,0xC5,0x5E,0x14,0x8B, +0x1F,0x1F,0x85,0xDB,0x75,0x02,0x0C,0x02,0xF7,0x46,0x38,0x10,0x00,0x74,0x02,0x0C, +0x04,0xF7,0x46,0x7A,0x02,0x00,0x74,0x02,0x0C,0x08,0x88,0x86,0xBF,0x00,0xC3,0x90, +0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x0D,0x9C,0x0E,0xE8,0x0B,0xEC,0x90,0xB0,0x02, +0xE6,0xDA,0xF8,0xC3,0x33,0xC0,0xE6,0xDA,0xF8,0xC3,0xB0,0x01,0xE6,0xD8,0xF8,0xC3, +0x33,0xC0,0xE6,0xD8,0xF8,0xC3,0xB0,0xFF,0xE8,0x68,0xFA,0xE8,0xBB,0xFA,0xF8,0xC3, +0xAC,0x49,0xE8,0xC9,0xFB,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x2F,0xFD,0xF8,0xC3,0x90, +0xAC,0x49,0xE8,0x81,0xFD,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x39,0xFD,0xF8,0xC3,0x90, +0xAC,0x49,0xE6,0x34,0xF8,0xC3,0xAC,0x49,0xE6,0x36,0xF8,0xC3,0xAC,0x49,0x3C,0x02, +0x77,0x1F,0x84,0xC0,0x75,0x1D,0xE4,0x14,0x24,0xEF,0xE6,0x14,0xE4,0x12,0x24,0x3F, +0xE6,0x12,0xE4,0x16,0xA8,0x04,0x74,0x09,0xE8,0x04,0xFA,0x72,0x04,0xB0,0x18,0xE6, +0x0A,0xF8,0xC3,0x8A,0xE0,0xE4,0x14,0x0C,0x10,0xE6,0x14,0xE4,0x12,0x0C,0xC0,0xF6, +0xC4,0x01,0x74,0x02,0x24,0x7F,0xE6,0x12,0xF8,0xC3,0xAC,0x49,0xE8,0x3F,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x40,0xE8,0x97,0xFD,0xE8,0xCE,0xFD,0xE8,0xC2,0xFE,0xB0,0x01, +0xE8,0xD3,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x9F,0xFD,0xE8,0xBA,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x10,0xE8,0x77,0xFD,0xE8,0xAE,0xFD,0xE8,0xA2,0xFE,0xB0,0x08, +0xE8,0xB3,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x7F,0xFD,0xE8,0x9A,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x80,0xE8,0x57,0xFD,0xE8,0x8E,0xFD,0xE8,0x82,0xFE,0xB0,0x02, +0xE8,0x93,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x5F,0xFD,0xE8,0x7A,0xFD,0xF8, +0xC3,0x90,0xB8,0x00,0x20,0xE8,0x37,0xFD,0xE8,0x6E,0xFD,0xE8,0x62,0xFE,0xB0,0x04, +0xE8,0x73,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x3F,0xFD,0xE8,0x5A,0xFD,0xF8, +0xC3,0x90,0xAC,0x49,0xE8,0x48,0x14,0xE4,0x3C,0x24,0xE7,0x0A,0xC4,0xE6,0x3C,0xF8, +0xC3,0x90,0xB8,0xDE,0x3B,0x89,0x46,0x7C,0xE4,0x3C,0x0C,0x18,0xE6,0x3C,0xF8,0xC3, +0xE4,0x12,0x0C,0x02,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFD,0xEB,0xF6,0xE8,0xCF, +0xFC,0xF8,0xC3,0x90,0x83,0x66,0x38,0xFD,0xF8,0xC3,0xAC,0x49,0xA8,0x01,0x74,0x06, +0x83,0x4E,0x7A,0x20,0xEB,0x04,0x83,0x66,0x7A,0xDF,0xE8,0xE5,0xFB,0xF8,0xC3,0x90, +0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x81,0x4E, +0x26,0x01,0x20,0xAC,0x49,0x32,0xE4,0x89,0x46,0x6E,0x83,0x4E,0x48,0x08,0x49,0x46, +0xF9,0xC3,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x81,0x4E,0x26,0x01,0x20,0xAC,0xB4,0x0A,0xF6,0xE4,0xEB,0xD8,0xE8,0xFA,0x13,0xE4, +0x3C,0x24,0xF8,0x0A,0xC4,0xE6,0x3C,0xF8,0xC3,0x90,0xAD,0x49,0x49,0x89,0x46,0x64, +0xA9,0x01,0x00,0x74,0x1B,0x8B,0xD8,0x83,0xE3,0xFA,0x75,0x1A,0xA9,0x04,0x00,0x74, +0x0F,0xE4,0x3E,0x0C,0x02,0xE6,0x3E,0xB8,0x1A,0x44,0x89,0x46,0x62,0xF8,0xC3,0x90, +0xE4,0x3E,0x24,0xFC,0xEB,0xEF,0xE4,0x3E,0x24,0xFC,0xE6,0x3E,0xE8,0x02,0xFD,0xB8, +0x8C,0x40,0xEB,0xE6,0xE8,0x88,0xF8,0x72,0x05,0xB0,0x18,0xE6,0x0A,0xF8,0xC3,0x90, +0xAC,0x49,0xE8,0xE9,0xF9,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0xE9,0xF9,0xF8,0xC3,0x90, +0xE8,0x82,0xFD,0x75,0x06,0x32,0xC0,0xE6,0xDA,0xF8,0xC3,0xB0,0x02,0xE6,0xDA,0x36, +0xA0,0xB4,0x13,0x24,0x10,0x34,0x10,0xE8,0x16,0x01,0x36,0xA1,0xB4,0x13,0xA9,0x01, +0x00,0x74,0x05,0xE8,0xFC,0xFE,0xEB,0x0E,0xA9,0x02,0x00,0x74,0x04,0x32,0xC0,0xEB, +0x02,0xB0,0x01,0xE8,0xDE,0xFE,0x36,0xA1,0xB4,0x13,0xE8,0xB5,0x13,0xE4,0x3C,0x24, +0xF8,0x0A,0xC4,0xE6,0x3C,0x36,0xA1,0xB4,0x13,0xC1,0xE8,0x05,0x25,0x01,0x00,0xE8, +0xFA,0xFE,0x36,0xA0,0xB5,0x13,0x24,0x10,0xE8,0x73,0xFB,0x32,0xC0,0x36,0x8A,0x26, +0xB5,0x13,0xF6,0xC4,0x04,0x74,0x09,0xFE,0xC0,0xF6,0xC4,0x08,0x74,0x02,0xFE,0xC0, +0xE8,0xDB,0xFD,0x36,0xA1,0xB6,0x13,0x25,0x0F,0x00,0xE8,0x71,0xF9,0x36,0xA1,0xB6, +0x13,0xC1,0xE8,0x04,0x25,0x03,0x00,0xE8,0xD2,0xFA,0x36,0xA1,0xB6,0x13,0xC1,0xE8, +0x05,0x25,0x02,0x00,0xE8,0x1F,0xFB,0x36,0xA1,0xB6,0x13,0xF6,0xC4,0x01,0x75,0x04, +0x32,0xC0,0xEB,0x09,0x80,0xE4,0x02,0xD0,0xEC,0xB0,0x02,0x2A,0xC4,0xE8,0xC6,0xFA, +0x36,0xF6,0x06,0xB7,0x13,0x40,0x74,0x05,0xE8,0x83,0xFE,0xEB,0x03,0xE8,0x84,0xFE, +0x36,0xF6,0x06,0xB7,0x13,0x20,0x74,0x05,0xE8,0x65,0xFE,0xEB,0x03,0xE8,0x68,0xFE, +0xF8,0xC3,0xE4,0x12,0x0C,0x01,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFE,0xEB,0xF6, +0xE4,0x14,0x24,0xF0,0x0C,0x05,0xE6,0x14,0xE4,0x2A,0x24,0xF0,0x0C,0x06,0xE6,0x2A, +0xF8,0xC3,0xE4,0x2A,0x24,0xF0,0xE6,0x2A,0xE4,0x14,0x24,0xF0,0x0C,0x07,0xE6,0x14, +0xF8,0xC3,0xAD,0x49,0x49,0xE8,0x7E,0xF9,0x89,0x86,0x8E,0x00,0xF8,0xC3,0xAD,0x49, +0x49,0xE8,0x72,0xF9,0x89,0x86,0x90,0x00,0xF8,0xC3,0x83,0x4E,0x26,0x04,0xE8,0xC2, +0xF7,0xF8,0xC3,0x90,0x83,0x66,0x26,0xFB,0xE8,0xB8,0xF7,0xF8,0xC3,0x90,0xAC,0x49, +0x84,0xC0,0x75,0x0D,0xE4,0x10,0x24,0xEF,0xE6,0x10,0x80,0x8E,0xA1,0x00,0x42,0xF8, +0xC3,0xE4,0x10,0x0C,0x10,0xEB,0xF1,0x90,0xAC,0x49,0x3C,0x02,0x76,0x02,0x32,0xC0, +0xC0,0xE0,0x04,0xA8,0x20,0x74,0x02,0x0C,0x08,0x24,0x18,0x8A,0xE0,0xE4,0x12,0x24, +0xE7,0x0A,0xC4,0xE6,0x12,0x80,0x8E,0xA1,0x00,0x44,0xF8,0xC3,0xAC,0x49,0x88,0x86, +0xC0,0x00,0xF8,0xC3,0xAC,0x49,0xE6,0x3A,0xF8,0xC3,0xAC,0x49,0x84,0xC0,0x74,0x08, +0xE4,0x12,0x0C,0x04,0xE6,0x12,0xF8,0xC3,0xE4,0x12,0x24,0xFB,0xEB,0xF6,0xAC,0x49, +0xE8,0xF0,0xF6,0x73,0x03,0xE8,0x41,0xF7,0xF8,0xC3,0xE4,0x12,0xA8,0x02,0x74,0x04, +0x24,0xFD,0xE6,0x12,0xB8,0xF0,0x00,0xE8,0xA1,0xFA,0x81,0x66,0x26,0xFF,0xF3,0xE8, +0x71,0xF7,0xE8,0xB4,0xFA,0xF8,0xC3,0x90,0xB8,0x80,0x00,0xE8,0x71,0xFA,0x80,0x4E, +0x27,0x08,0xE8,0x5E,0xF7,0xE8,0xA1,0xFA,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x7B,0xFA, +0x81,0x66,0x26,0xFF,0xF7,0xE8,0x4B,0xF7,0xE8,0x8E,0xFA,0xF8,0xC3,0x90,0xB8,0x10, +0x00,0xE8,0x4B,0xFA,0x80,0x4E,0x27,0x04,0xE8,0x38,0xF7,0xE8,0x7B,0xFA,0xF8,0xC3, +0xB8,0x10,0x00,0xE8,0x55,0xFA,0x81,0x66,0x26,0xFF,0xFB,0xE8,0x25,0xF7,0xE8,0x68, +0xFA,0xF8,0xC3,0x90,0x33,0xC0,0xAC,0x49,0x3C,0x01,0x73,0x04,0xB0,0x01,0xEB,0x06, +0x3C,0x0C,0x76,0x02,0xB0,0x0C,0x89,0x46,0x1C,0xF8,0xC3,0x90,0x81,0x4E,0x26,0x00, +0x20,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x24,0xFB,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x83, +0x4E,0x26,0x01,0xF8,0xC3,0x90,0x81,0x4E,0x26,0x00,0x40,0x8A,0x86,0xA5,0x00,0x0C, +0x02,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xF8,0xC3,0x90,0xAC,0x49,0x50,0xE8,0x1F,0xF6, +0x58,0x72,0x08,0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xF8,0xC3,0xF9,0xC3,0x90,0xAC,0x50, +0xAD,0xE8,0x9C,0xF8,0x5A,0xF6,0xC2,0x01,0x74,0x12,0x39,0x86,0x96,0x00,0x74,0x0C, +0x89,0x86,0x96,0x00,0xE6,0xF0,0x86,0xE0,0xE6,0xF8,0x86,0xE0,0xF6,0xC2,0x02,0x74, +0x10,0x39,0x86,0x94,0x00,0x74,0x0A,0x89,0x86,0x94,0x00,0xE6,0xE4,0x86,0xE0,0xE6, +0xEC,0x83,0xE9,0x03,0xC3,0x90,0xE4,0x16,0x88,0x86,0xBC,0x00,0xE8,0x00,0xFB,0x33, +0xDB,0xE4,0x0C,0xA8,0x06,0x74,0x03,0x80,0xCB,0x01,0xA8,0x10,0x74,0x03,0x80,0xCB, +0x02,0xA8,0x80,0x74,0x03,0x80,0xCB,0x04,0xE4,0x12,0x8A,0xE0,0x24,0x18,0x0A,0xD8, +0xE4,0xDA,0xF6,0xC4,0x02,0x74,0x07,0xA8,0x40,0x75,0x03,0x80,0xCB,0x20,0xA8,0x02, +0x75,0x09,0xE4,0x2A,0xA8,0x0F,0x74,0x03,0x80,0xCB,0x40,0xF7,0x46,0x38,0x02,0x00, +0x74,0x09,0xE4,0xD8,0xA8,0x01,0x75,0x03,0x80,0xCB,0x80,0x88,0x9E,0xBE,0x00,0xFE, +0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x76,0xE4,0xF8,0xC3,0xAC,0x49,0x3C,0x02,0x74,0x41, +0x77,0x1F,0x50,0xE8,0x69,0xF5,0x58,0x72,0x0C,0x84,0xC0,0x74,0x0A,0xB0,0x12,0xE6, +0x0A,0x80,0x4E,0x38,0x01,0xF8,0xC3,0xB0,0x11,0xE6,0x0A,0x80,0x66,0x38,0xFE,0xF8, +0xC3,0x8B,0x46,0x38,0x25,0xFF,0xF7,0x89,0x46,0x38,0xA9,0x00,0x04,0x75,0xE6,0x8A, +0x86,0xA5,0x00,0xA8,0x10,0x75,0xDE,0x0C,0x10,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xF8, +0xC3,0x81,0x4E,0x38,0x00,0x08,0x8A,0x86,0xA5,0x00,0xA8,0x10,0x74,0xC7,0x24,0xEF, +0xEB,0xE7,0xAD,0x49,0x49,0x3C,0x01,0x72,0x11,0x3C,0x0C,0x77,0x0D,0x50,0x8A,0xE0, +0xE4,0x14,0x25,0xF0,0x0F,0x0A,0xC4,0xE6,0x14,0x58,0x8A,0xC4,0x84,0xC0,0x74,0x02, +0xE6,0x42,0xF8,0xC3,0xE8,0xE9,0xF9,0xFE,0x86,0xB9,0x00,0xB0,0x0E,0xE8,0xEE,0xE3, +0xF8,0xC3,0x3A,0x86,0xAF,0x00,0x74,0x1F,0x88,0x86,0xAF,0x00,0x8A,0xE0,0x80,0xC2, +0x06,0xB0,0xBF,0xEE,0x80,0xEA,0x02,0x8A,0xC4,0xEE,0x8A,0x86,0xA8,0x00,0x80,0xC2, +0x02,0xEE,0x80,0xEA,0x06,0x8A,0xC4,0xC3,0x8B,0x46,0x3E,0x85,0xC0,0x8A,0x86,0xA5, +0x00,0x74,0x12,0xA8,0x08,0x75,0x0D,0x0C,0x08,0x88,0x86,0xA5,0x00,0x80,0xC2,0x02, +0xEE,0x80,0xEA,0x02,0xC3,0xA8,0x08,0x74,0xFB,0x24,0xF7,0xEB,0xEC,0x8B,0x46,0x26, +0x84,0xC0,0x74,0x16,0x8A,0x86,0xA5,0x00,0xA8,0x02,0x74,0x0D,0x24,0xFD,0x88,0x86, +0xA5,0x00,0x83,0xC2,0x02,0xEE,0x83,0xEA,0x02,0xC3,0x8A,0x86,0xA5,0x00,0xA8,0x02, +0x75,0xF7,0x0C,0x02,0xEB,0xE8,0x52,0x83,0xC2,0x0C,0xEC,0xC0,0xE8,0x04,0x88,0x86, +0xA9,0x00,0x8B,0x5E,0x26,0x80,0xE3,0x3F,0xF6,0xC7,0x04,0x74,0x07,0xA8,0x08,0x75, +0x03,0x80,0xCB,0x40,0xF6,0xC7,0x08,0x74,0x07,0xA8,0x02,0x75,0x03,0x80,0xCB,0x80, +0x88,0x5E,0x26,0x8A,0x86,0xA5,0x00,0x84,0xDB,0x74,0x10,0xA8,0x02,0x74,0x0A,0x24, +0xFD,0x88,0x86,0xA5,0x00,0x83,0xEA,0x0A,0xEE,0x5A,0xC3,0xA8,0x02,0x75,0xFA,0x0C, +0x02,0xEB,0xEE,0x90,0xFF,0xFF,0x00,0x48,0x00,0x30,0xBA,0x20,0xC4,0x1A,0x00,0x18, +0x00,0x12,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x02,0x80,0x01,0xC0,0x00,0x60,0x00, +0x30,0x00,0x18,0x00,0xCD,0x01,0x00,0x01,0x80,0x00,0x10,0x00,0x10,0x00,0x0E,0x00, +0x0C,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x03,0x00,0x02,0x00, +0x01,0x00,0x52,0x51,0x56,0x3C,0x1E,0x77,0x47,0x98,0x8B,0xF0,0x8A,0x82,0xC4,0x00, +0x32,0xE4,0x83,0xFE,0x18,0x74,0x3D,0x83,0xFE,0x19,0x74,0x3E,0x83,0xFE,0x1E,0x77, +0x2F,0xD1,0xE6,0x2E,0x8B,0x8C,0x14,0x2D,0x3B,0x8E,0x94,0x00,0x74,0x22,0x89,0x8E, +0x94,0x00,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x8A,0xE0,0x0C,0x80,0xEE,0x83,0xEA, +0x06,0x8A,0xC1,0xEE,0x83,0xC2,0x02,0x8A,0xC5,0xEE,0x83,0xC2,0x04,0x8A,0xC4,0xEE, +0x5E,0x59,0x5A,0xC3,0x8B,0x8E,0x8E,0x00,0xEB,0xCE,0x8B,0x8E,0x90,0x00,0xEB,0xC8, +0x52,0x51,0x3D,0x05,0x00,0x77,0x03,0xB8,0x05,0x00,0x8B,0xC8,0xBA,0x02,0x00,0xB8, +0x00,0xD0,0xF7,0xF1,0x05,0x01,0x00,0xD1,0xE8,0x59,0x5A,0xC3,0x8B,0x46,0x7A,0xA8, +0x20,0x74,0x0B,0x80,0xBE,0xC3,0x00,0x03,0x75,0x04,0x0C,0x01,0xEB,0x02,0x24,0xFE, +0x89,0x46,0x7A,0xC3,0x24,0x03,0x88,0x86,0xC3,0x00,0x8A,0xA6,0xA8,0x00,0x8A,0xDC, +0x80,0xE4,0xFC,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83,0xC2,0x06, +0xEE,0x83,0xEA,0x06,0xE8,0xC5,0xFF,0xC3,0x00,0x08,0x18,0x38,0x28,0x90,0x3C,0x04, +0x77,0x23,0x32,0xE4,0x8B,0xD8,0x2E,0x8A,0x87,0x08,0x2E,0x8A,0xA6,0xA8,0x00,0x8A, +0xDC,0x80,0xE4,0xC7,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83,0xC2, +0x06,0xEE,0x83,0xEA,0x06,0xC3,0x84,0xC0,0x74,0x02,0xB0,0x04,0x8A,0xA6,0xA8,0x00, +0x8A,0xDC,0x80,0xE4,0xFB,0x0A,0xC4,0x3A,0xC3,0x74,0x0B,0x88,0x86,0xA8,0x00,0x83, +0xC2,0x06,0xEE,0x83,0xEA,0x06,0xC3,0x90,0x8B,0x5E,0x38,0x84,0xC0,0x74,0x34,0x3C, +0x02,0x74,0x3B,0x8A,0x86,0xAF,0x00,0x0C,0x04,0xE8,0xE6,0xFD,0x8B,0x46,0x2E,0x3B, +0x46,0x3C,0x77,0x1B,0xF7,0xC3,0x00,0x04,0x75,0x15,0x81,0xCB,0x00,0x04,0x83,0xC2, +0x02,0x8A,0x86,0xA5,0x00,0x24,0xFA,0x88,0x86,0xA5,0x00,0xEE,0x83,0xEA,0x02,0x89, +0x5E,0x38,0xC3,0x8A,0x86,0xAF,0x00,0x24,0xFB,0xE8,0xB6,0xFD,0xEB,0xF1,0xF7,0xC3, +0x10,0x00,0x74,0xEF,0xEB,0xED,0x83,0xC2,0x0C,0xEC,0x83,0xEA,0x0C,0xC0,0xE8,0x04, +0x88,0x86,0xA9,0x00,0xC3,0x90,0x8A,0x86,0xA7,0x00,0x0C,0x01,0x88,0x86,0xA7,0x00, +0x8B,0xDA,0x80,0xC2,0x08,0xEE,0x8B,0xD3,0xF8,0xC3,0x8A,0x86,0xA7,0x00,0x24,0xFE, +0xEB,0xEA,0x8A,0x86,0xA7,0x00,0x0C,0x02,0xEB,0xE2,0x8A,0x86,0xA7,0x00,0x24,0xFD, +0xEB,0xDA,0xB0,0xFF,0xE8,0x6C,0xF2,0xE8,0xB1,0xF2,0xF8,0xC3,0xAC,0x49,0xE8,0x61, +0xFE,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0xEB,0xFE,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x35, +0xFF,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x05,0xFF,0xF8,0xC3,0x90,0x52,0x83,0xC2,0x06, +0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x02,0xAC,0x49,0xEE,0x5A,0x8A,0x86,0xA8,0x00,0xEE, +0x5A,0xF8,0xC3,0x90,0x52,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x06,0xEB, +0xE6,0x90,0xAC,0x49,0x3C,0x02,0x77,0x0D,0x84,0xC0,0x75,0x0B,0x8A,0x86,0xAF,0x00, +0x24,0xFD,0xE8,0x0D,0xFD,0xF8,0xC3,0x50,0x8A,0x86,0xAF,0x00,0x0C,0x02,0xE8,0x01, +0xFD,0x5B,0x83,0xC2,0x08,0x8A,0x86,0xA7,0x00,0xF6,0xC3,0x01,0x74,0x0C,0x24,0xDF, +0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA,0x08,0xF8,0xC3,0x0C,0x20,0xEB,0xF2,0xAC,0x49, +0xE8,0xE5,0xFE,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x83,0xF5,0xE8,0xF9,0xFC,0xE8, +0x24,0xFF,0xB0,0x01,0xE8,0xBF,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x40,0xE8,0x8B,0xF5, +0xE8,0xE5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x63,0xF5,0xE8,0xD9,0xFC,0xE8, +0x04,0xFF,0xB0,0x08,0xE8,0x9F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x10,0xE8,0x6B,0xF5, +0xE8,0xC5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x43,0xF5,0xE8,0xB9,0xFC,0xE8, +0xE4,0xFE,0xB0,0x02,0xE8,0x7F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x80,0xE8,0x4B,0xF5, +0xE8,0xA5,0xFC,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x23,0xF5,0xE8,0x99,0xFC,0xE8, +0xC4,0xFE,0xB0,0x04,0xE8,0x5F,0xF6,0xF8,0xC3,0x90,0xB8,0x00,0x20,0xE8,0x2B,0xF5, +0xE8,0x85,0xFC,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x34,0x0C,0xF8,0xC3,0x90,0xB8,0xDE, +0x3B,0x89,0x46,0x7C,0xF8,0xC3,0x8A,0x86,0xAF,0x00,0x0C,0x80,0xE8,0x43,0xFC,0xF8, +0xC3,0x90,0x8A,0x86,0xAF,0x00,0x24,0x7F,0xEB,0xF2,0x8A,0x86,0xAF,0x00,0x0C,0x40, +0xE8,0x2F,0xFC,0xF8,0xC3,0x90,0x8A,0x86,0xAF,0x00,0x24,0xBF,0xEB,0xF2,0xAC,0x49, +0xA8,0x01,0x74,0x07,0x83,0x4E,0x7A,0x20,0xEB,0x05,0x90,0x83,0x66,0x7A,0xDF,0xE8, +0x8A,0xFD,0xF8,0xC3,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8, +0x00,0xEE,0x83,0xEA,0x06,0xAC,0x49,0x32,0xE4,0x89,0x46,0x6E,0x83,0x4E,0x26,0x01, +0x83,0x4E,0x48,0x08,0xB0,0x06,0xE8,0xD5,0xDF,0x49,0x46,0xF9,0xC3,0x90,0x83,0xC2, +0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8,0x00,0xEE,0x83,0xEA,0x06,0xAC, +0xB4,0x0A,0xF6,0xE4,0xEB,0xD0,0xE8,0xE0,0x0B,0xF8,0xC3,0x90,0xAD,0x49,0x49,0x89, +0x46,0x64,0xA9,0x01,0x00,0x74,0x19,0x8B,0xD8,0x83,0xE3,0xFA,0x75,0x0A,0xA9,0x04, +0x00,0x74,0x0D,0xB8,0xC4,0x3F,0xEB,0x0B,0xE8,0x06,0xF5,0xB8,0x8C,0x40,0xEB,0x03, +0xB8,0x1A,0x44,0x89,0x46,0x62,0xF8,0xC3,0x8A,0x86,0xAF,0x00,0xA8,0x02,0x74,0x0A, +0x24,0xFD,0xE8,0x8D,0xFB,0x0C,0x02,0xE8,0x88,0xFB,0xF8,0xC3,0xAC,0x49,0xE8,0x81, +0xFC,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x79,0xFC,0xF8,0xC3,0x90,0xE8,0x76,0xF5,0x75, +0x05,0xE8,0xE6,0xFD,0xF8,0xC3,0xE8,0xCD,0xFD,0x36,0xA0,0xB4,0x13,0x24,0x10,0x34, +0x10,0xE8,0x26,0x01,0x36,0xA1,0xB4,0x13,0xA9,0x01,0x00,0x74,0x05,0xE8,0xFE,0xFE, +0xEB,0x0E,0xA9,0x02,0x00,0x74,0x04,0x32,0xC0,0xEB,0x02,0xB0,0x01,0xE8,0xE8,0xFE, +0x36,0xA1,0xB4,0x13,0xE8,0xAB,0x0B,0x36,0xA1,0xB4,0x13,0xC1,0xE8,0x05,0x25,0x01, +0x00,0xE8,0x0C,0xFF,0x36,0xA0,0xB5,0x13,0x24,0x10,0xE8,0x2B,0xFD,0x32,0xC0,0x36, +0x8A,0x26,0xB5,0x13,0xF6,0xC4,0x04,0x74,0x09,0xFE,0xC0,0xF6,0xC4,0x08,0x74,0x02, +0xFE,0xC0,0xE8,0xEF,0xFD,0x36,0xA1,0xB6,0x13,0x25,0x0F,0x00,0xE8,0x03,0xFC,0x36, +0xA1,0xB6,0x13,0xC1,0xE8,0x04,0x25,0x03,0x00,0xE8,0x88,0xFC,0x36,0xA1,0xB6,0x13, +0xC1,0xE8,0x05,0x25,0x02,0x00,0xE8,0xCD,0xFC,0x36,0xA1,0xB6,0x13,0xF6,0xC4,0x01, +0x75,0x04,0x32,0xC0,0xEB,0x09,0x80,0xE4,0x02,0xD0,0xEC,0xB0,0x02,0x2A,0xC4,0xE8, +0x8C,0xFC,0x36,0xF6,0x06,0xB7,0x13,0x40,0x74,0x05,0xE8,0x8D,0xFE,0xEB,0x03,0xE8, +0x94,0xFE,0x36,0xF6,0x06,0xB7,0x13,0x20,0x74,0x05,0xE8,0x69,0xFE,0xEB,0x03,0xE8, +0x70,0xFE,0xF8,0xC3,0xF8,0xC3,0x8B,0x46,0x38,0xA9,0x04,0x00,0x75,0x23,0x0D,0x04, +0x00,0x89,0x46,0x38,0x83,0xC2,0x08,0x8B,0x46,0x2E,0x3B,0x46,0x3C,0x73,0x14,0x83, +0x4E,0x38,0x10,0x8A,0x86,0xA7,0x00,0x24,0xFE,0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA, +0x08,0xF8,0xC3,0x8A,0x86,0xA7,0x00,0x0C,0x01,0xEB,0xEE,0x90,0x8B,0x46,0x38,0xA9, +0x04,0x00,0x74,0x06,0x25,0xFB,0xFF,0x89,0x46,0x38,0xF8,0xC3,0xAD,0x49,0x49,0xE8, +0xBE,0xFB,0x89,0x86,0x8E,0x00,0xF8,0xC3,0xAD,0x49,0x49,0xE8,0xB2,0xFB,0x89,0x86, +0x90,0x00,0xF8,0xC3,0x83,0x4E,0x26,0x04,0xE8,0x92,0xFA,0xF8,0xC3,0x90,0x83,0x66, +0x26,0xFB,0xE8,0x88,0xFA,0xF8,0xC3,0x90,0xAC,0x49,0x84,0xC0,0x75,0x07,0x80,0x8E, +0xA3,0x00,0x04,0xF8,0xC3,0x80,0xA6,0xA3,0x00,0xFB,0xF8,0xC3,0xAC,0x49,0x83,0xC2, +0x08,0x3C,0x02,0x76,0x02,0x32,0xC0,0x3C,0x01,0x74,0x12,0x77,0x0B,0x8A,0x86,0xA7, +0x00,0x24,0xEF,0x88,0x86,0xA7,0x00,0xEE,0x83,0xEA,0x08,0xF8,0xC3,0x8A,0x86,0xA7, +0x00,0x0C,0x10,0xEB,0xEE,0x90,0x52,0x83,0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2, +0x04,0xAC,0x49,0xEE,0x5A,0x8A,0x86,0xA8,0x00,0xEE,0x5A,0xF8,0xC3,0x90,0x52,0x83, +0xC2,0x06,0xB0,0xBF,0xEE,0x52,0x83,0xC2,0x08,0xEB,0xE6,0x90,0xAC,0x49,0xF8,0xC3, +0xAC,0x49,0xE8,0xCE,0xEE,0x73,0x03,0xE8,0x11,0xEF,0xF8,0xC3,0x8A,0x86,0xAF,0x00, +0x24,0x7F,0xE8,0xBD,0xF9,0xB8,0xF0,0x00,0xE8,0x80,0xF2,0x81,0x66,0x26,0xFF,0xF3, +0xE8,0x23,0xFA,0xE8,0xD2,0xF9,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x51,0xF2,0x80,0x4E, +0x27,0x08,0xE8,0x11,0xFA,0xE8,0xC0,0xF9,0xF8,0xC3,0xB8,0x80,0x00,0xE8,0x5B,0xF2, +0x81,0x66,0x26,0xFF,0xF7,0xE8,0xFE,0xF9,0xE8,0xAD,0xF9,0xF8,0xC3,0x90,0xB8,0x10, +0x00,0xE8,0x2B,0xF2,0x80,0x4E,0x27,0x04,0xE8,0xEB,0xF9,0xE8,0x9A,0xF9,0xF8,0xC3, +0xB8,0x10,0x00,0xE8,0x19,0xF2,0x81,0x66,0x26,0xFF,0xFB,0xE8,0xD8,0xF9,0xF8,0xC3, +0xAC,0x49,0xF8,0xC3,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x0C,0x40,0x88,0x86,0xA8, +0x00,0xEE,0x83,0xEA,0x06,0xF8,0xC3,0x90,0x83,0xC2,0x06,0x8A,0x86,0xA8,0x00,0x24, +0xBF,0xEB,0xEA,0x90,0xAC,0x49,0x8A,0xE0,0x80,0xC2,0x0A,0xEC,0x80,0xEA,0x0A,0xA8, +0x20,0x74,0x05,0x8A,0xC4,0xEE,0xF8,0xC3,0x06,0x51,0x57,0x8B,0x4E,0x24,0xE3,0x34, +0x49,0x89,0x4E,0x24,0xFF,0x46,0x1A,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x8A,0xC4,0xAA, +0x89,0x7E,0x22,0x8B,0x46,0x26,0x24,0xFD,0x89,0x46,0x26,0x75,0x29,0x8A,0x86,0xA5, +0x00,0xA8,0x02,0x75,0x21,0x80,0xC2,0x02,0x0C,0x02,0x88,0x86,0xA5,0x00,0xEE,0x80, +0xEA,0x02,0xEB,0x12,0xC4,0x7E,0x00,0x3B,0x7E,0x1E,0x76,0x0A,0x4F,0x26,0x88,0x25, +0x89,0x7E,0x00,0xFF,0x46,0x1A,0x5F,0x59,0x07,0xF8,0xC3,0x90,0xAC,0xAD,0x83,0xE9, +0x03,0x85,0xC0,0x74,0x05,0x3D,0x00,0x20,0x72,0x05,0xB8,0xFF,0xFF,0xEB,0x03,0xC1, +0xE0,0x03,0x3B,0x86,0x94,0x00,0x74,0x26,0x89,0x86,0x94,0x00,0x8B,0xD8,0x52,0x83, +0xC2,0x06,0x8A,0x86,0xA8,0x00,0x8A,0xE0,0x0C,0x80,0xEE,0x83,0xEA,0x06,0x8A,0xC3, +0xEE,0x83,0xC2,0x02,0x8A,0xC7,0xEE,0x83,0xC2,0x04,0x8A,0xC4,0xEE,0x5A,0xF8,0xC3, +0xB0,0x88,0x88,0x86,0xBC,0x00,0xE8,0xA6,0xF2,0x33,0xDB,0x8A,0x86,0xA5,0x00,0xA8, +0x02,0x74,0x03,0x80,0xCB,0x01,0xA8,0x05,0x74,0x03,0x80,0xCB,0x02,0xA8,0x08,0x74, +0x03,0x80,0xCB,0x04,0xF6,0x86,0xA7,0x00,0x10,0x74,0x03,0x80,0xCB,0x10,0x8A,0x86, +0xA9,0x00,0xF6,0xC3,0x04,0x75,0x0A,0x83,0xC2,0x0C,0xEC,0x83,0xEA,0x0C,0xC0,0xE8, +0x04,0x8A,0xE0,0x8A,0x86,0xAF,0x00,0xA8,0x80,0x74,0x08,0xF6,0xC4,0x01,0x75,0x03, +0x80,0xCB,0x20,0xF6,0x86,0xA7,0x00,0x02,0x75,0x0A,0xF7,0x46,0x38,0x04,0x00,0x74, +0x03,0x80,0xCB,0x40,0x88,0x9E,0xBE,0x00,0xFE,0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x0D, +0xDC,0xF8,0xC3,0xFE,0x86,0xB4,0x00,0xB0,0x0A,0xE8,0x02,0xDC,0xF8,0xC3,0xAC,0x49, +0x3C,0x02,0x74,0x37,0x77,0x10,0x84,0xC0,0x74,0x06,0x80,0x4E,0x38,0x01,0xF8,0xC3, +0x80,0x66,0x38,0xFE,0xF8,0xC3,0x8B,0x46,0x38,0x25,0xFF,0xF7,0x89,0x46,0x38,0xA9, +0x00,0x04,0x75,0xEA,0x8A,0x86,0xA5,0x00,0xA8,0x01,0x75,0xE2,0x0C,0x05,0x83,0xC2, +0x02,0x88,0x86,0xA5,0x00,0xEE,0x83,0xEA,0x02,0xF8,0xC3,0x81,0x4E,0x38,0x00,0x08, +0x8A,0x86,0xA5,0x00,0xA8,0x01,0x74,0xC6,0x24,0xFA,0xEB,0xE2,0xAD,0x49,0x49,0xF8, +0xC3,0x90,0xE8,0x11,0xFA,0xFE,0x86,0xB9,0x00,0xB0,0x0E,0xE8,0xA0,0xDB,0xF8,0xC3, +0xB0,0xFF,0xE8,0xD9,0xEC,0xF8,0xC3,0x90,0x83,0x66,0x7A,0xFB,0xB0,0x00,0xE8,0x8D, +0xDB,0xF8,0xC3,0x90,0xAC,0x49,0xE8,0x6D,0xD9,0x72,0x11,0x36,0x88,0x1E,0x1A,0x01, +0x36,0xA0,0x8E,0x12,0x0A,0xC3,0x52,0xBA,0x00,0x01,0xEE,0x5A,0xF8,0xC3,0xAC,0x49, +0x32,0xE4,0x36,0xA3,0x86,0x12,0x05,0x06,0x00,0x36,0x8B,0x1E,0x88,0x12,0x2B,0xD8, +0x36,0x89,0x1E,0x8A,0x12,0xF8,0xC3,0x90,0xAD,0x8B,0xD8,0xAD,0x83,0xE9,0x04,0x03, +0xC3,0x2B,0x46,0x76,0x89,0x46,0x78,0xF7,0x46,0x7A,0x02,0x00,0x74,0x0A,0x83,0x66, +0x7A,0xFD,0xB8,0x00,0x00,0xE8,0x36,0xDB,0xF8,0xC3,0x06,0x16,0x07,0xAC,0x49,0x25, +0x0F,0x00,0x6B,0xC0,0x09,0x8D,0xBE,0xFD,0x00,0x03,0xF8,0xAC,0x49,0x25,0x0F,0x00, +0xAA,0x85,0xC0,0x74,0x08,0x2B,0xC8,0x51,0x8B,0xC8,0xF3,0xA4,0x59,0xE8,0x41,0xF0, +0xE8,0x44,0x03,0x07,0xF8,0xC3,0x33,0xC0,0xAC,0x49,0x36,0xA3,0xB2,0x13,0x36,0xA3, +0xB0,0x13,0xF8,0xC3,0x83,0x66,0x7A,0xEF,0xE8,0x2C,0x03,0xF8,0xC3,0x90,0x83,0x4E, +0x7A,0x10,0xEB,0xF4,0xE8,0xB5,0xF0,0xF8,0xC3,0x90,0xAD,0x3C,0x19,0x77,0x0E,0x3C, +0x19,0x77,0x0A,0x8B,0xF8,0x81,0xE7,0xFF,0x00,0x88,0xA6,0xC4,0x00,0xF8,0xC3,0x90, +0x83,0x4E,0x26,0x20,0xAC,0x49,0x32,0xE4,0xD1,0xE0,0x8B,0xD8,0xC1,0xE3,0x02,0x03, +0xC3,0x89,0x46,0x6E,0x83,0x4E,0x48,0x04,0xB0,0x06,0xE8,0xB1,0xDA,0x49,0x46,0xF9, +0xC3,0x90,0xFE,0x86,0xB3,0x00,0xB0,0x0A,0xE8,0xA3,0xDA,0xF8,0xC3,0x90,0x33,0xC0, +0xAC,0x49,0x6B,0xC0,0x0A,0x89,0x86,0x8A,0x00,0xF8,0xC3,0x90,0xAC,0x49,0x32,0xE4, +0x3D,0x0A,0x00,0x77,0x05,0xB8,0x0A,0x00,0xEB,0x08,0x3D,0x5A,0x00,0x72,0x03,0xB8, +0x5A,0x00,0x51,0xF7,0xD8,0x05,0x64,0x00,0x8B,0xC8,0x8B,0x46,0x44,0xF7,0xE1,0xB9, +0x64,0x00,0xF7,0xF1,0x89,0x46,0x46,0x59,0xF8,0xC3,0xAC,0x49,0xE8,0x9F,0xEB,0xF8, +0xC3,0x90,0xAC,0x49,0x84,0xC0,0x75,0x07,0x81,0x66,0x38,0xFF,0xFD,0xF8,0xC3,0x81, +0x4E,0x38,0x00,0x02,0xF7,0x46,0x38,0x40,0x00,0x75,0x08,0x8A,0x86,0xA9,0x00,0x88, +0x86,0xAA,0x00,0xF8,0xC3,0x90,0x51,0x56,0xE8,0x7F,0x0C,0x5E,0x59,0xF8,0xC3,0x90, +0xFE,0x86,0xB6,0x00,0xB0,0x0A,0xE8,0x25,0xDA,0xF8,0xC3,0x90,0xFE,0x86,0xB7,0x00, +0xB0,0x0A,0xE8,0x19,0xDA,0xF8,0xC3,0x90,0xFE,0x86,0xB8,0x00,0xB0,0x0A,0xE8,0x0D, +0xDA,0xF8,0xC3,0x90,0x00,0x90,0x51,0x55,0xAC,0x2E,0xA2,0x34,0x36,0x33,0xC9,0xAD, +0x8B,0xF9,0xC1,0xE7,0x05,0xA9,0x01,0x00,0x74,0x23,0x2E,0x8B,0xAD,0x44,0x00,0x83, +0x7E,0x08,0x00,0x74,0x18,0x2E,0x80,0x3E,0x34,0x36,0x01,0x74,0x09,0x60,0xB0,0x04, +0xE8,0xBB,0x0C,0x61,0xEB,0x07,0x60,0xB0,0xFB,0xE8,0xEC,0x0C,0x61,0x47,0x47,0xD1, +0xE8,0x75,0xD2,0x41,0x83,0xF9,0x04,0x72,0xC6,0x5D,0x59,0x83,0xE9,0x05,0xF7,0x46, +0x38,0x40,0x00,0x74,0x05,0xE8,0xA1,0xEA,0xF8,0xC3,0xE8,0xA7,0xEA,0xF8,0xC3,0x90, +0x36,0xC6,0x06,0xC8,0x13,0x01,0xF8,0xC3,0x33,0xC0,0xAC,0x49,0x36,0xA3,0x80,0x12, +0xAC,0x49,0x36,0x2B,0x06,0x88,0x12,0xF7,0xD8,0x36,0xA3,0x82,0x12,0xF8,0xC3,0x90, +0xC0,0x26,0xC0,0x26,0xCE,0x26,0xD4,0x26,0xDA,0x26,0xE0,0x26,0xE6,0x26,0xF0,0x26, +0xF8,0x26,0x00,0x27,0x08,0x27,0x10,0x27,0x16,0x27,0xA0,0x34,0xA8,0x34,0xB4,0x34, +0x1C,0x27,0x5A,0x27,0x62,0x27,0x76,0x27,0x82,0x27,0x96,0x27,0xA2,0x27,0xB6,0x27, +0xC2,0x27,0xD6,0x27,0xE2,0x27,0xF2,0x27,0xCE,0x34,0xC0,0x26,0x00,0x28,0x08,0x28, +0x0E,0x28,0x14,0x28,0x1A,0x28,0x30,0x28,0x6C,0x28,0xE8,0x34,0x0A,0x35,0x7A,0x28, +0xA0,0x28,0xB4,0x28,0xC0,0x28,0xC8,0x28,0x36,0x35,0x44,0x35,0x4E,0x35,0xD0,0x28, +0xA2,0x29,0xAA,0x29,0xB0,0x29,0xC2,0x29,0x54,0x35,0x5A,0x35,0xD2,0x29,0xDE,0x29, +0x70,0x35,0xEA,0x29,0xF4,0x29,0xFE,0x29,0x92,0x35,0x18,0x2A,0x9E,0x35,0x3C,0x2A, +0x44,0x2A,0x4A,0x2A,0xAC,0x35,0x5E,0x2A,0xDA,0x35,0x6A,0x2A,0x88,0x2A,0x9A,0x2A, +0xAE,0x2A,0xC0,0x2A,0xD4,0x2A,0xE2,0x35,0xEC,0x2A,0x06,0x2B,0x06,0x36,0x1A,0x2B, +0x2E,0x2B,0x66,0x2B,0x10,0x36,0x1C,0x36,0x28,0x36,0x36,0x36,0xCA,0x2B,0x90,0x36, +0x22,0x2C,0x44,0x2C,0x98,0x36,0x52,0x28,0xC0,0x26,0xC0,0x26,0xB6,0x2E,0xCA,0x2E, +0xD2,0x2E,0xDA,0x2E,0xE2,0x2E,0xEC,0x2E,0xF4,0x2E,0xFC,0x2E,0x04,0x2F,0x0C,0x2F, +0x24,0x2F,0xA0,0x34,0xA8,0x34,0xB4,0x34,0x32,0x2F,0x6E,0x2F,0x76,0x2F,0x8A,0x2F, +0x96,0x2F,0xAA,0x2F,0xB6,0x2F,0xCA,0x2F,0xD6,0x2F,0xEA,0x2F,0xF6,0x2F,0xFE,0x2F, +0xCE,0x34,0xC0,0x26,0x06,0x30,0x12,0x30,0x1A,0x30,0x26,0x30,0x2E,0x30,0x44,0x30, +0x86,0x30,0xE8,0x34,0x0A,0x35,0x8C,0x30,0xB0,0x30,0xB8,0x30,0xCC,0x30,0xD4,0x30, +0x36,0x35,0x44,0x35,0x4E,0x35,0xDC,0x30,0xA4,0x31,0xA4,0x31,0xA6,0x31,0xDC,0x31, +0x54,0x35,0x5A,0x35,0xEC,0x31,0xF8,0x31,0x70,0x35,0x04,0x32,0x0E,0x32,0x18,0x32, +0x92,0x35,0x2C,0x32,0x9E,0x35,0x56,0x32,0x6E,0x32,0x7C,0x32,0xAC,0x35,0x80,0x32, +0xDA,0x35,0x8C,0x32,0xA8,0x32,0xBA,0x32,0xCE,0x32,0xE0,0x32,0xF0,0x32,0xE2,0x35, +0xF4,0x32,0x08,0x33,0x06,0x36,0x14,0x33,0x7C,0x33,0xC0,0x33,0x10,0x36,0x1C,0x36, +0x28,0x36,0x36,0x36,0x3E,0x34,0x90,0x36,0x8C,0x34,0x92,0x34,0x98,0x36,0x6E,0x30, +0xE3,0x28,0xF7,0x46,0x38,0x40,0x00,0x75,0x32,0xE8,0xFD,0xE8,0x33,0xC0,0xAC,0x49, +0x3D,0x5B,0x00,0x77,0x19,0x8B,0xD8,0xD1,0xE3,0x2E,0xFF,0x97,0xB0,0x36,0x72,0x0B, +0x85,0xC9,0x75,0xE8,0x8B,0x46,0x48,0xE8,0x1A,0x0C,0xC3,0x4E,0x41,0xC3,0x6A,0x00, +0x1F,0xC6,0x06,0x93,0x12,0x0C,0x9C,0x0E,0xE8,0x7D,0xDA,0xE8,0xD6,0xE8,0x33,0xC0, +0xAC,0x49,0x3D,0x5B,0x00,0x77,0xE7,0x8B,0xD8,0xD1,0xE3,0x2E,0xFF,0x97,0x68,0x37, +0x72,0xD9,0x85,0xC9,0x75,0xE8,0xC3,0xF7,0x46,0x7A,0x10,0x00,0x75,0x0F,0x83,0xBE, +0x84,0x00,0x00,0x74,0x08,0xB8,0x2A,0x3A,0x89,0x86,0x80,0x00,0xC3,0x81,0xBE,0x80, +0x00,0xCE,0x3C,0x74,0xF7,0x83,0xBE,0x88,0x00,0x00,0x75,0x05,0xB8,0xCE,0x3C,0xEB, +0xE7,0xF7,0x46,0x7A,0x08,0x00,0x75,0x40,0x1E,0x60,0x8B,0x8E,0x88,0x00,0x3B,0x4E, +0x74,0x77,0x33,0x3B,0x4E,0x78,0x77,0x2E,0xC4,0x7E,0x10,0x8B,0xDF,0x26,0x03,0x3D, +0x47,0x47,0x33,0xC0,0x8E,0xD8,0x8D,0xB6,0xF4,0x00,0x8B,0xC1,0xF7,0x46,0x7A,0x01, +0x00,0x75,0x1D,0xF3,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46, +0x74,0xB0,0x0C,0xE8,0x58,0xD7,0x61,0x1F,0xC7,0x86,0x88,0x00,0x00,0x00,0xEB,0xAC, +0xE3,0xE3,0x50,0x90,0xAC,0x24,0x7F,0xAA,0xE2,0xFA,0x58,0xEB,0xD8,0x90,0x8B,0x8E, +0x88,0x00,0xE3,0x46,0x8B,0x9E,0x8A,0x00,0x85,0xDB,0x74,0x3E,0xBA,0x50,0xFF,0xED, +0x2B,0x86,0x82,0x00,0x3B,0xC3,0x72,0x37,0x8D,0xB6,0xF4,0x00,0xC4,0x7E,0x10,0x8B, +0xDF,0x26,0x03,0x3D,0x47,0x47,0x8B,0xC1,0x16,0x1F,0xF7,0x46,0x7A,0x01,0x00,0x75, +0x24,0xF3,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0xC7, +0x86,0x88,0x00,0x00,0x00,0xB0,0x0C,0xE8,0xF4,0xD6,0x83,0x66,0x7A,0xF7,0xC3,0xB0, +0x00,0xE8,0xEA,0xD6,0xC3,0xE3,0xDC,0x50,0xAC,0x24,0x7F,0xAA,0xE2,0xFA,0x58,0xEB, +0xD2,0x90,0x1E,0x60,0x33,0xC0,0x8E,0xD8,0x8D,0xB6,0xFD,0x00,0x8B,0x86,0x88,0x00, +0x8B,0x96,0x84,0x00,0x3A,0x04,0x75,0x10,0x8B,0xDE,0x46,0x8B,0xC8,0x8D,0xBE,0xF4, +0x00,0xF3,0xA6,0x74,0x66,0x8B,0xF3,0x90,0x83,0xC6,0x09,0x4A,0x75,0xE6,0x8D,0xB6, +0xFD,0x00,0x8B,0x96,0x84,0x00,0x3A,0x04,0x73,0x10,0x8B,0xDE,0x46,0x8B,0xC8,0x8D, +0xBE,0xF4,0x00,0xF3,0xA6,0x74,0x76,0x8B,0xF3,0x90,0x83,0xC6,0x09,0x4A,0x75,0xE6, +0x8D,0xB6,0xF4,0x00,0xAC,0xF7,0x46,0x7A,0x01,0x00,0x74,0x02,0x24,0x7F,0x1E,0xC5, +0x5E,0x10,0x8B,0x37,0x88,0x40,0x02,0x46,0x89,0x37,0xFF,0x4E,0x78,0xFF,0x46,0x76, +0xFF,0x4E,0x74,0x1F,0x8B,0x8E,0x88,0x00,0x49,0x89,0x8E,0x88,0x00,0xE3,0x43,0x8D, +0xB6,0xF4,0x00,0x8B,0xFE,0x46,0xF3,0xA4,0xE9,0x7D,0xFF,0xC5,0x76,0x10,0x8B,0x1C, +0x85,0xDB,0x74,0x08,0x03,0xF3,0x83,0xC6,0x03,0x83,0xE6,0xFE,0x8B,0x86,0x84,0x00, +0x2B,0xC2,0xB4,0x80,0x89,0x04,0x46,0x46,0xC7,0x04,0x00,0x00,0x89,0x76,0x10,0x83, +0x4E,0x7A,0x04,0xC7,0x86,0x88,0x00,0x00,0x00,0x61,0x1F,0xF9,0xC3,0x33,0xC0,0x61, +0x1F,0xC3,0xB0,0x80,0x84,0xC0,0x61,0x1F,0xC3,0x90,0x8B,0x4E,0x78,0x2B,0x8E,0x88, +0x00,0x76,0x27,0x89,0xB6,0x8C,0x00,0x8B,0x5E,0x74,0x3B,0xCB,0x72,0x02,0x8B,0xCB, +0x3B,0xC8,0x72,0x02,0x8B,0xC8,0x8B,0xC1,0xE3,0x44,0x33,0xD2,0x8E,0xC2,0x8B,0xD1, +0x83,0xBE,0x88,0x00,0x00,0x74,0x06,0xE9,0x8E,0x00,0x33,0xC0,0xC3,0x8B,0x5E,0x10, +0x03,0x1F,0x43,0x43,0x52,0xF7,0x46,0x7A,0x01,0x00,0x75,0x2A,0xAC,0x8D,0xBE,0xE4, +0x00,0x8B,0x8E,0x86,0x00,0xF2,0xAE,0x74,0x34,0x88,0x07,0x43,0x4A,0x75,0xED,0x58, +0x8B,0x5E,0x10,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0x8B,0xC6, +0x2B,0x86,0x8C,0x00,0xC3,0x90,0xAC,0x8D,0xBE,0xE4,0x00,0x8B,0x8E,0x86,0x00,0xF2, +0xAE,0x74,0x0A,0x24,0x7F,0x88,0x07,0x43,0x4A,0x75,0xEB,0xEB,0xD2,0x88,0x86,0xF4, +0x00,0xC7,0x86,0x88,0x00,0x01,0x00,0x58,0x2B,0xC2,0x74,0x0E,0x8B,0x5E,0x10,0x01, +0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0x40,0xE8,0x94,0xFE,0x72,0xBE, +0x4A,0x75,0x15,0x83,0xBE,0x8A,0x00,0x00,0x74,0xB4,0xBA,0x50,0xFF,0xED,0x89,0x86, +0x82,0x00,0x83,0x4E,0x7A,0x08,0xEB,0xA6,0x8D,0xBE,0xF4,0x00,0x03,0xBE,0x88,0x00, +0xA4,0xFF,0x86,0x88,0x00,0xE8,0x6A,0xFE,0x72,0x94,0x79,0x06,0x4A,0x74,0x8F,0xE9, +0x5B,0xFF,0x4A,0x74,0xCE,0xEB,0xE1,0x90,0x50,0xE8,0x2B,0xCC,0x8B,0x46,0x74,0x39, +0x46,0x72,0x74,0x27,0x1E,0x56,0x51,0x33,0xC9,0xC5,0x76,0x0C,0xAD,0x74,0x10,0x78, +0x09,0x03,0xC8,0x05,0x01,0x00,0x24,0xFE,0x03,0xF0,0x3B,0x76,0x10,0x76,0xED,0x29, +0x4E,0x76,0x01,0x4E,0x78,0xE8,0x51,0xCC,0x59,0x5E,0x1F,0x58,0xC3,0x90,0xC4,0x7E, +0x10,0x26,0x8B,0x1D,0x83,0xC3,0x03,0x26,0x89,0x1D,0x4B,0x03,0xFB,0xAB,0x91,0xAA, +0xB8,0x03,0x00,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74,0xC3,0x90,0xC4,0x7E, +0x10,0x26,0x8B,0x1D,0x43,0x26,0x89,0x1D,0x43,0x03,0xFB,0xAA,0xFF,0x4E,0x78,0xFF, +0x46,0x76,0xFF,0x4E,0x74,0xC3,0xE8,0xE5,0xFF,0xC3,0x80,0x81,0x84,0x85,0x82,0x83, +0x86,0x87,0x50,0x53,0x8A,0xDC,0x83,0xE3,0x0E,0xD1,0xEB,0x2E,0x8A,0x87,0x7A,0x3B, +0x08,0x86,0xB0,0x00,0xFE,0x86,0xB1,0x00,0xB0,0x0A,0xE8,0xA1,0xD4,0x5B,0x58,0xC3, +0x50,0x8A,0xC8,0xB8,0xFF,0x00,0xE8,0x95,0xFF,0x58,0xC3,0x90,0x8A,0x86,0xBB,0x00, +0xE8,0xAB,0xFF,0xC3,0xE8,0xCB,0xFF,0xE8,0xF2,0xFF,0xC3,0x90,0xE8,0xC3,0xFF,0xE8, +0xB4,0xFF,0xC3,0x90,0x33,0xC0,0xE8,0x95,0xFF,0xC3,0xB8,0xFF,0x00,0x33,0xC9,0xE8, +0x6C,0xFF,0xC3,0x90,0xB8,0xFF,0x01,0xB1,0x10,0xE8,0x62,0xFF,0xC3,0x90,0xC3,0xDE, +0x3B,0xC4,0x3B,0xD4,0x3B,0xD4,0x3B,0xDE,0x3B,0xC4,0x3B,0xCA,0x3B,0xCA,0x3B,0xDE, +0x3B,0xC4,0x3B,0xCA,0x3B,0xCA,0x3B,0xDE,0x3B,0xC4,0x3B,0xC4,0x3B,0xC4,0x3B,0x00, +0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, +0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, +0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x51, +0x53,0x8B,0x4E,0x38,0x81,0xE1,0xFF,0xEE,0xA8,0x04,0x74,0x04,0x81,0xC9,0x00,0x01, +0x8A,0xE0,0x80,0xE4,0x03,0x24,0x18,0xD0,0xE4,0x0A,0xC4,0x33,0xDB,0x8A,0xD8,0x2E, +0x8B,0x87,0xDF,0x3B,0x89,0x46,0x7C,0x2E,0x0B,0x8F,0xFF,0x3B,0x89,0x4E,0x38,0xD1, +0xEB,0x2E,0x8A,0xA7,0x1F,0x3C,0x5B,0x59,0xC3,0xAC,0x49,0x3C,0x01,0x72,0x1D,0x74, +0x20,0x3C,0x03,0x72,0x23,0x74,0x28,0x3C,0x08,0x72,0x2B,0x74,0x30,0x3C,0x20,0x72, +0x37,0x74,0x3A,0xBB,0xBC,0x3B,0x32,0xE4,0x89,0x5E,0x7E,0xC3,0xBB,0x82,0x3B,0xEB, +0xF5,0xBB,0x76,0x3B,0xB4,0x01,0xEB,0xF0,0xBB,0xDE,0x3B,0xB4,0x02,0xEB,0xE9,0xBB, +0xC4,0x3B,0xB4,0x03,0xEB,0xE2,0xBB,0xA0,0x3B,0xB4,0x04,0xEB,0xDB,0xBB,0xAC,0x3B, +0xAC,0x49,0x88,0x86,0xBB,0x00,0xEB,0xCE,0xBB,0xB4,0x3B,0xEB,0xF3,0xBB,0xDE,0x3B, +0xEB,0xC4,0xA9,0x04,0x00,0x75,0xD1,0xA9,0x08,0x00,0x75,0xDA,0xEB,0xD1,0x8B,0x5E, +0x74,0x8B,0x4E,0x78,0x3B,0xCB,0x72,0x02,0x8B,0xCB,0x3B,0xC8,0x72,0x02,0x8B,0xC8, +0x8B,0xC1,0xE3,0x2C,0xC4,0x7E,0x10,0x8B,0xDF,0x26,0x03,0x3D,0x47,0x47,0xF7,0x46, +0x7A,0x01,0x00,0x75,0x1C,0xF7,0xC7,0x01,0x00,0x74,0x02,0x49,0xA4,0xD1,0xE9,0xF3, +0xA5,0x73,0x01,0xA4,0x26,0x01,0x07,0x29,0x46,0x78,0x01,0x46,0x76,0x29,0x46,0x74, +0xC3,0x50,0x53,0xBB,0x7F,0x7F,0xF7,0xC7,0x01,0x00,0x74,0x05,0x49,0xAC,0x22,0xC3, +0xAA,0xD1,0xE9,0xE3,0x1D,0x9C,0xAD,0x23,0xC3,0xAB,0x49,0x74,0x14,0xAD,0x23,0xC3, +0xAB,0x49,0x74,0x0D,0xAD,0x23,0xC3,0xAB,0x49,0x74,0x06,0xAD,0x23,0xC3,0xAB,0xE2, +0xE5,0x9D,0x73,0x04,0xAC,0x22,0xC3,0xAB,0x5B,0x58,0xEB,0xB8,0xE8,0xE8,0xC9,0x8B, +0x5E,0x38,0xF7,0xC3,0x10,0x04,0x75,0x01,0xC3,0xF7,0xC3,0x40,0x00,0x74,0x05,0xE8, +0xD2,0xE3,0xEB,0x03,0xE8,0xC2,0xE3,0x81,0x66,0x38,0xEF,0xFB,0xF6,0xC3,0x10,0x74, +0x3C,0xF6,0xC3,0x02,0x74,0x06,0xE4,0xD8,0x0C,0x01,0xE6,0xD8,0xF6,0xC3,0x04,0x74, +0x11,0x83,0xC2,0x08,0x8A,0x86,0xA7,0x00,0x0C,0x01,0xEE,0x88,0x86,0xA7,0x00,0x83, +0xEA,0x08,0xF6,0xC3,0x08,0x74,0x0F,0xE8,0xA5,0xE3,0x72,0x0A,0x8A,0x86,0xC0,0x00, +0xE6,0x38,0xB0,0x23,0xE6,0x0A,0xF7,0xC3,0x00,0x04,0x75,0x01,0xC3,0xF7,0xC3,0x00, +0x08,0x75,0xF9,0x8A,0x86,0xA5,0x00,0xF6,0xC3,0x40,0x75,0x0D,0xA8,0x10,0x75,0xEC, +0x0C,0x10,0x88,0x86,0xA5,0x00,0xE6,0x0C,0xC3,0xA8,0x01,0x75,0xDF,0x83,0xC2,0x02, +0x0C,0x05,0xEE,0x88,0x86,0xA5,0x00,0xC3,0xB0,0x00,0xE8,0x61,0xD2,0xEB,0x0F,0xB0, +0x02,0xE8,0x90,0x0E,0xEB,0x08,0x83,0x66,0x38,0xDF,0x83,0x4E,0x7A,0x02,0x33,0xC0, +0x8E,0xD8,0xFA,0xA0,0x92,0x12,0x40,0xA2,0x92,0x12,0x3C,0x05,0x72,0x1E,0xC6,0x06, +0x92,0x12,0x00,0xFB,0xB0,0x01,0xE8,0x6B,0x0E,0xFA,0xA1,0x26,0x01,0x23,0x06,0x2A, +0x01,0xA8,0x01,0x75,0x07,0xE8,0xE2,0x07,0xE8,0x61,0x09,0x90,0xB0,0x00,0xE8,0x51, +0xD2,0xFB,0x85,0xED,0x74,0xB9,0xFA,0xF7,0x46,0x7A,0x46,0x00,0x75,0xC0,0x8B,0x46, +0x78,0x3D,0x0A,0x00,0x72,0xB0,0x8B,0x4E,0x74,0x83,0xF9,0x50,0x72,0x9A,0x83,0x66, +0x38,0xDF,0xC5,0x76,0x14,0x8B,0x46,0x3A,0x85,0xC0,0x75,0x58,0xAD,0x85,0xC0,0x75, +0x0F,0xE8,0xF8,0xFE,0xF7,0x46,0x7A,0x08,0x00,0x74,0x93,0xE8,0xA0,0xFA,0xEB,0x8E, +0x3B,0x76,0x04,0x76,0x21,0xB9,0x02,0x00,0x39,0x4E,0x2E,0x77,0x05,0xC7,0x46,0x2E, +0x00,0x00,0x56,0x8B,0x76,0x2C,0x89,0x76,0x04,0xC7,0x04,0x00,0x00,0x46,0x46,0x89, +0x76,0x2C,0x29,0x4E,0x2E,0x5E,0x85,0xC0,0x79,0x17,0xF6,0xC4,0x10,0x74,0x05,0xFF, +0x56,0x7C,0xEB,0x03,0xFF,0x56,0x7E,0x89,0x76,0x14,0xB0,0x0C,0xE8,0x9F,0xD1,0xEB, +0x86,0x89,0x46,0x3A,0xFF,0x96,0x80,0x00,0x29,0x46,0x3A,0x89,0x76,0x14,0xB0,0x0C, +0xE8,0x8B,0xD1,0xE9,0x71,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x04, +0x10,0x02,0x01,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0, +0xC0,0x80,0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x30,0x41,0x5A,0x41,0xB2,0x41,0xD6,0x41,0xE8,0x41, +0xFA,0x41,0xC3,0x90,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x89,0x7E,0x6C,0x80,0x66,0x27, +0xFD,0x8B,0x56,0x24,0x83,0xFA,0x04,0x72,0xE9,0x83,0xEA,0x02,0x8B,0xD9,0x3B,0xCA, +0x76,0x02,0x8B,0xCA,0xB0,0x0A,0x57,0x51,0x8B,0xFE,0xF2,0xAE,0x8B,0xC1,0x59,0x5F, +0x75,0x1E,0x50,0x40,0x2B,0xC8,0x74,0x06,0x2B,0xD1,0x2B,0xD9,0xF3,0xA4,0x59,0x4B, +0x4A,0x4A,0xB0,0x0D,0xAA,0xA4,0x3B,0xCA,0x76,0x02,0x8B,0xCA,0xE3,0x13,0xEB,0xD4, +0x2B,0xD9,0xF7,0xC6,0x01,0x00,0x74,0x02,0xA4,0x49,0xD1,0xE9,0xF3,0xA5,0x73,0x01, +0xA4,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x29,0x7E,0x24,0x01,0x7E,0x1A,0x8B,0xCB,0x80, +0x7E,0x26,0x02,0x74,0x05,0x80,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD,0xE8,0x18,0x03, +0x61,0xC3,0xC3,0x90,0xE8,0x7C,0x02,0x72,0xF9,0x90,0x83,0x4E,0x26,0x20,0x8B,0x46, +0x6A,0x89,0x46,0x6E,0x8B,0x46,0x48,0x0D,0x04,0x00,0x25,0xBF,0xFF,0x89,0x46,0x48, +0xB0,0x06,0xE8,0xD9,0xCF,0xC3,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x01,0x7E,0x1A,0x29, +0x7E,0x24,0x80,0x7E,0x26,0x02,0x74,0x05,0x83,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD, +0xE8,0xD5,0x02,0x61,0xC3,0x90,0x8A,0xBE,0xC2,0x00,0xEB,0x24,0xF7,0x46,0x48,0x40, +0x00,0x75,0xB1,0x8E,0x46,0x02,0x8B,0x7E,0x22,0x89,0x7E,0x6C,0x8B,0x56,0x24,0x83, +0xEA,0x0A,0x78,0x9E,0x03,0xD7,0x80,0x66,0x27,0xFD,0x33,0xC0,0x8A,0xBE,0xC2,0x00, +0xE3,0xB4,0x3B,0xFA,0x77,0xB0,0xAC,0x49,0x93,0x2E,0x8A,0x87,0xB6,0x3E,0x93,0x22, +0xDF,0x75,0x17,0xAA,0xE3,0xA0,0x3B,0xFA,0x77,0x9C,0xAC,0x49,0x93,0x2E,0x8A,0x87, +0xB6,0x3E,0x93,0x22,0xDF,0x75,0x03,0xAA,0xEB,0xD6,0xF6,0xC3,0x7F,0x75,0x05,0xFF, +0x46,0x66,0xEB,0xDF,0xF6,0xC3,0x40,0x75,0x0C,0x8B,0xD8,0x83,0xEB,0x08,0xD1,0xE3, +0x2E,0xFF,0xA7,0xB6,0x3F,0xFF,0x46,0x66,0x2C,0x20,0xEB,0xC7,0x85,0xC0,0x74,0x2C, +0x89,0x46,0x6A,0x83,0x4E,0x48,0x40,0x89,0x7E,0x22,0x2B,0x7E,0x6C,0x01,0x7E,0x1A, +0x29,0x7E,0x24,0x80,0x7E,0x26,0x02,0x74,0x08,0x83,0x66,0x26,0xFD,0xE8,0xA3,0x01, +0xC3,0x60,0xB0,0xFD,0xE8,0x31,0x02,0x61,0xE8,0x98,0x01,0xC3,0xE9,0x57,0xFF,0x90, +0x8B,0x5E,0x66,0x4B,0x78,0x03,0x89,0x5E,0x66,0xAA,0x8B,0x5E,0x64,0xF7,0xC3,0x00, +0x20,0x75,0x03,0xE9,0x40,0xFF,0xF7,0xC3,0x40,0x00,0x74,0x08,0x8A,0x86,0xC1,0x00, +0xAA,0xE9,0x32,0xFF,0xB8,0x32,0x00,0xEB,0xA3,0x90,0x8B,0x5E,0x66,0x89,0x5E,0x68, +0x83,0xC3,0x08,0x80,0xE3,0xF8,0x89,0x5E,0x66,0x8B,0x5E,0x64,0x81,0xE3,0x00,0x18, +0x81,0xFB,0x00,0x18,0x74,0x2D,0xAA,0x85,0xDB,0x74,0x25,0xF7,0x46,0x64,0x40,0x00, +0x75,0x18,0x81,0xFB,0x00,0x10,0x74,0x0C,0x8B,0x46,0x66,0x2B,0x46,0x68,0xC1,0xE0, +0x04,0xE9,0x68,0xFF,0xB8,0x64,0x00,0xE9,0x62,0xFF,0x8A,0x86,0xC1,0x00,0xAA,0xAA, +0xE9,0xE3,0xFE,0x51,0x8B,0x4E,0x66,0x2B,0x4E,0x68,0xB0,0x20,0xF3,0xAA,0x59,0xE9, +0xD4,0xFE,0x8B,0x5E,0x66,0x89,0x5E,0x68,0x8B,0x5E,0x64,0xF7,0xC3,0x24,0x00,0x74, +0x10,0xC7,0x46,0x66,0x00,0x00,0xF7,0xC3,0x04,0x00,0x74,0x05,0xB0,0x0D,0xAA,0xB0, +0x0A,0xAA,0xEB,0x48,0x90,0x90,0xAA,0xF7,0x46,0x64,0x00,0x40,0x74,0x06,0xB8,0xD0, +0x07,0xE9,0x18,0xFF,0xE9,0x9F,0xFE,0x90,0xAA,0xF7,0x46,0x64,0x00,0x80,0x74,0x06, +0xB8,0xD0,0x07,0xE9,0x06,0xFF,0xE9,0x8D,0xFE,0x90,0x8B,0x5E,0x66,0x89,0x5E,0x68, +0x85,0xDB,0x75,0x0C,0x8B,0x5E,0x64,0xF7,0xC3,0x10,0x00,0x74,0x06,0xE9,0x76,0xFE, +0x8B,0x5E,0x64,0xF7,0xC3,0x08,0x00,0x74,0x27,0xB0,0x0A,0xAA,0xF7,0xC3,0x20,0x00, +0x75,0x1F,0xF7,0xC3,0x00,0x01,0x75,0x03,0xE9,0x5B,0xFE,0xF7,0xC3,0x40,0x00,0x75, +0x06,0xB8,0x64,0x00,0xE9,0xC5,0xFE,0x8A,0x86,0xC1,0x00,0xAA,0xAA,0xE9,0x46,0xFE, +0xAA,0xC7,0x46,0x66,0x00,0x00,0xF7,0xC3,0x00,0x06,0x74,0xF1,0xF7,0xC3,0x40,0x00, +0x74,0x19,0x8A,0x86,0xC1,0x00,0x81,0xE3,0x00,0x06,0x81,0xFB,0x00,0x04,0x72,0x06, +0x76,0x02,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xE9,0x1B,0xFE,0x81,0xE3,0x00,0x06,0x81, +0xFB,0x00,0x04,0x72,0x0E,0x76,0x06,0xB8,0x96,0x00,0xE9,0x7F,0xFE,0xB8,0x64,0x00, +0xE9,0x79,0xFE,0x8B,0x46,0x68,0xE9,0x73,0xFE,0x90,0x36,0x8B,0x0E,0xDA,0x12,0x83, +0xF9,0x32,0x73,0x1D,0x1E,0x06,0x33,0xC0,0x8E,0xD8,0x8E,0xC0,0x8D,0x76,0x4C,0xBF, +0xDC,0x12,0x03,0xF9,0xA5,0xA5,0xA5,0x83,0xC1,0x06,0x89,0x0E,0xDA,0x12,0x07,0x1F, +0xC3,0xB0,0x08,0xE8,0x88,0xCD,0xC3,0x90,0x83,0x66,0x48,0xFE,0xE8,0xAD,0xC4,0xE8, +0xC8,0xFF,0xC3,0xF6,0x46,0x27,0x02,0x75,0x0F,0x9C,0xFA,0x83,0x7E,0x1A,0x00,0x74, +0x09,0x80,0x4E,0x27,0x01,0x9D,0xF9,0xC3,0xF8,0xC3,0x50,0x52,0xF7,0x46,0x38,0x40, +0x00,0x74,0x1D,0xE8,0x4E,0xDE,0x83,0xC2,0x0A,0xEC,0xA8,0x40,0x75,0x27,0x83,0xEA, +0x08,0x8A,0x86,0xA5,0x00,0x0C,0x02,0x88,0x86,0xA5,0x00,0xEE,0x5A,0x58,0xEB,0xD1, +0xE8,0x26,0xDE,0x8A,0x86,0xA5,0x00,0x24,0xFB,0x0C,0x02,0x88,0x86,0xA5,0x00,0xE6, +0x0C,0x5A,0x58,0xEB,0xBC,0x80,0x4E,0x27,0x02,0x5A,0x58,0x9D,0xF8,0xC3,0x08,0x46, +0x26,0x9C,0xFA,0x8A,0x8E,0xA5,0x00,0xF7,0x46,0x38,0x40,0x00,0x75,0x14,0xF6,0xC1, +0x06,0x74,0x23,0xE8,0xF3,0xDD,0x8A,0xC1,0x24,0xF9,0x88,0x86,0xA5,0x00,0xE6,0x0C, +0x9D,0xC3,0xF6,0xC1,0x02,0x74,0x0F,0xE8,0xEA,0xDD,0x83,0xC2,0x02,0x8A,0xC1,0x24, +0xFD,0x88,0x86,0xA5,0x00,0xEE,0x9D,0xC3,0x8B,0x5E,0x26,0x22,0xC3,0x88,0x46,0x26, +0x74,0x01,0xC3,0x80,0x66,0x27,0xFD,0x9C,0xFA,0x8A,0x8E,0xA5,0x00,0xF7,0x46,0x38, +0x40,0x00,0x75,0x16,0xF6,0xC1,0x04,0x75,0x0F,0xE8,0xAD,0xDD,0x8A,0xC1,0x24,0xFD, +0x0C,0x04,0x88,0x86,0xA5,0x00,0xE6,0x0C,0x9D,0xC3,0xF6,0xC1,0x02,0x75,0xF9,0xE8, +0xA2,0xDD,0x83,0xC2,0x0A,0xEC,0xA8,0x20,0x75,0x0E,0x83,0xEA,0x08,0x8A,0xC1,0x0C, +0x02,0x88,0x86,0xA5,0x00,0xEE,0x9D,0xC3,0x83,0xEA,0x0A,0x33,0xC9,0x8A,0x4E,0x1C, +0x8B,0x46,0x1A,0x3B,0xC8,0x73,0x1B,0x01,0x4E,0x2A,0x2B,0xC1,0x89,0x46,0x1A,0x1E, +0xC5,0x76,0x00,0xF3,0x6E,0x1F,0x89,0x76,0x00,0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00, +0xEB,0xCD,0x85,0xC0,0x74,0x12,0x01,0x46,0x2A,0x8B,0xC8,0x1E,0xC5,0x76,0x00,0xF3, +0x6E,0x1F,0x89,0x76,0x00,0x89,0x4E,0x1A,0xF6,0xC7,0x01,0x75,0x23,0x80,0xCB,0x02, +0x89,0x5E,0x26,0xE8,0x22,0xC3,0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00,0x24,0xFD,0xEE, +0x88,0x86,0xA5,0x00,0xF6,0xC7,0x10,0x75,0x05,0xB0,0x02,0xE8,0x30,0xCC,0x9D,0xC3, +0x83,0xC2,0x02,0x8A,0x86,0xA5,0x00,0xEB,0x86,0x90,0x8B,0xD1,0x8B,0x46,0x24,0x3B, +0xC8,0x76,0x02,0x8B,0xC8,0x2B,0xD1,0x2B,0xC1,0x8B,0xD9,0xE3,0x22,0x80,0x66,0x27, +0xFD,0x8E,0x46,0x02,0x8B,0x7E,0x22,0xF7,0xC6,0x01,0x00,0x74,0x02,0xA4,0x49,0xD1, +0xE9,0xF3,0xA5,0x73,0x01,0xA4,0x89,0x7E,0x22,0x89,0x46,0x24,0x01,0x5E,0x1A,0x8B, +0xCA,0x80,0x7E,0x26,0x02,0x74,0x05,0x80,0x66,0x26,0xFD,0xC3,0x60,0xB0,0xFD,0xE8, +0xF6,0xFE,0x61,0xC3,0x50,0xE4,0x0A,0x84,0xC0,0x75,0x0A,0x86,0x86,0xA1,0x00,0x84, +0xC0,0x74,0x0A,0xE6,0x0A,0x58,0x0C,0x20,0x89,0x46,0x48,0xF9,0xC3,0x58,0x24,0xDF, +0x89,0x46,0x48,0xF8,0xC3,0x90,0xFB,0xB0,0x02,0xE8,0xE8,0x07,0xFA,0xE8,0x2E,0x01, +0xFB,0xB0,0x01,0xE8,0xDE,0x07,0xFA,0xB0,0x02,0xE8,0xD6,0xCB,0xFB,0x85,0xED,0x74, +0xE5,0xFA,0x8E,0x5E,0x0A,0xFB,0x90,0xFA,0x8B,0x46,0x48,0x8B,0x76,0x40,0xA8,0x8C, +0x75,0xDE,0xA8,0x20,0x74,0x1A,0x50,0xE8,0x6F,0xDC,0x58,0xE8,0xA6,0xFF,0x73,0x10, +0xB0,0x02,0xE8,0x79,0xCB,0xEB,0xC9,0x90,0x25,0xFF,0x00,0x8B,0xC8,0xEB,0x36,0x90, +0xA8,0x01,0x75,0x22,0x46,0x83,0xE6,0xFE,0x3B,0x76,0x08,0x74,0x79,0xAD,0x8A,0xFC, +0xB3,0xF0,0x22,0xFB,0x3A,0xFB,0x74,0xE0,0x3A,0xBE,0xA0,0x00,0x74,0x2E,0xE8,0xD2, +0xFD,0x73,0x77,0xEB,0x9B,0x90,0x8A,0xE0,0x24,0xFC,0x88,0x46,0x48,0x8B,0x4E,0x4A, +0xF6,0xC4,0x02,0x74,0x1D,0xE8,0xBB,0xFD,0x72,0x86,0xE8,0x13,0xF3,0x89,0x76,0x40, +0xE3,0x93,0x83,0x4E,0x48,0x03,0x89,0x4E,0x4A,0xE9,0x74,0xFF,0x25,0xFF,0x0F,0x8B, +0xC8,0x90,0x8B,0x86,0x98,0x00,0x85,0xC0,0x74,0x1A,0x51,0x8A,0x8E,0xA0,0x00,0xC0, +0xE9,0x04,0xBA,0x01,0x00,0xD3,0xE2,0x59,0x23,0xC2,0x74,0x08,0x03,0xF1,0x89,0x76, +0x40,0xE9,0x61,0xFF,0xFF,0x56,0x62,0xE3,0xF5,0x83,0x4E,0x48,0x01,0x89,0x4E,0x4A, +0x89,0x76,0x40,0xE9,0x3A,0xFF,0x81,0x4E,0x26,0x00,0x10,0x8B,0x46,0x50,0x3B,0x46, +0x46,0x77,0x03,0xE8,0x52,0xFD,0xE9,0x27,0xFF,0x90,0x88,0xBE,0xA0,0x00,0xEB,0xAC, +0x0A,0x06,0x90,0x12,0x8A,0xE0,0xBA,0x06,0x01,0xB0,0x04,0xEE,0xEC,0x84,0xC0,0x75, +0x12,0xB0,0x04,0xEE,0x8A,0xC4,0xEE,0x32,0xE4,0xA8,0x80,0x74,0x06,0xC7,0x06,0x84, +0x12,0x00,0x00,0x88,0x26,0x90,0x12,0xC3,0x0A,0x06,0x90,0x12,0x8A,0xE0,0xBA,0x06, +0x01,0xEC,0xA8,0x01,0x75,0xED,0xBA,0x08,0x01,0x8A,0xC4,0xEE,0x32,0xE4,0xA8,0x80, +0x74,0xE1,0xC7,0x06,0x84,0x12,0x00,0x00,0x88,0x26,0x90,0x12,0xC3,0x90,0x36,0xF7, +0x06,0x24,0x01,0x01,0x00,0x75,0x30,0x36,0x8B,0x0E,0xDA,0x12,0x80,0xF9,0x36,0x73, +0x26,0x33,0xC0,0x8E,0xC0,0x8E,0xD8,0xBF,0xDC,0x12,0x03,0xF9,0xB0,0x08,0xE8,0x91, +0xCA,0x85,0xED,0x74,0x0E,0x8D,0x76,0x4C,0xA5,0xA5,0xA5,0x80,0xC1,0x06,0x80,0xF9, +0x36,0x72,0xE9,0x89,0x0E,0xDA,0x12,0xC3,0xC3,0x90,0xF7,0x06,0x26,0x01,0x01,0x00, +0x75,0xF6,0x8B,0x0E,0x20,0x13,0x85,0xC9,0x75,0xEE,0x33,0xC0,0x8E,0xC0,0x8E,0xD8, +0xBF,0x24,0x13,0xB9,0x36,0x00,0xB0,0x0A,0xE8,0x57,0xCA,0x85,0xED,0x75,0x06,0xE9, +0x12,0x01,0xE9,0x0A,0x01,0x33,0xDB,0x8A,0x46,0x4C,0x8A,0xA6,0xB3,0x00,0xFE,0xCC, +0x78,0x0E,0x88,0xA6,0xB3,0x00,0x0A,0xDC,0xB4,0x0A,0xAB,0x83,0xE9,0x02,0x76,0xE2, +0x8A,0xA6,0xB2,0x00,0xFE,0xCC,0x78,0x0E,0x88,0xA6,0xB2,0x00,0x0A,0xDC,0xB4,0x08, +0xAB,0x83,0xE9,0x02,0x76,0xCC,0x8A,0xA6,0xB1,0x00,0xFE,0xCC,0x78,0x18,0x8A,0xBE, +0xB0,0x00,0x75,0x04,0x88,0xA6,0xB0,0x00,0x88,0xA6,0xB1,0x00,0x0A,0xDC,0x8A,0xE7, +0xAB,0x83,0xE9,0x02,0x76,0xAC,0x8A,0xA6,0xB4,0x00,0xFE,0xCC,0x78,0x1F,0x88,0xA6, +0xB4,0x00,0x0A,0xDC,0xB4,0x0B,0xAB,0x8A,0x86,0xBC,0x00,0x8A,0xA6,0xBD,0x00,0xAB, +0x8B,0x86,0xBE,0x00,0xAB,0x83,0xE9,0x06,0x76,0x88,0x8A,0x46,0x4C,0x8A,0xA6,0xB6, +0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB6,0x00,0x0A,0xDC,0xB4,0x0C,0xAB,0xE8,0xF5, +0xCB,0xAB,0x8B,0x46,0x2A,0xAB,0x83,0xE9,0x06,0x76,0x74,0x8A,0x46,0x4C,0x8A,0xA6, +0xB7,0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB7,0x00,0x0A,0xDC,0xB4,0x0D,0xAB,0xE8, +0xD4,0xCB,0xAB,0x8B,0x46,0x34,0xAB,0x83,0xE9,0x06,0x76,0x53,0x8A,0x46,0x4C,0x8A, +0xA6,0xB8,0x00,0xFE,0xCC,0x78,0x19,0x88,0xA6,0xB8,0x00,0x0A,0xDC,0xB4,0x0E,0xAB, +0xA1,0x50,0x12,0xAB,0xA1,0x52,0x12,0xAB,0x83,0xE9,0x06,0x76,0x32,0x8A,0x46,0x4C, +0x8A,0xA6,0xB5,0x00,0xFE,0xCC,0x78,0x18,0x88,0xA6,0xB5,0x00,0x0A,0xDC,0xB4,0x0F, +0xAB,0x8B,0x86,0x9A,0x00,0xAB,0x8B,0x86,0x9C,0x00,0xAB,0x83,0xE9,0x06,0x76,0x0F, +0x84,0xDB,0x75,0x03,0xE9,0xEF,0xFE,0xB0,0x0A,0xE8,0x12,0xC9,0xE9,0xE7,0xFE,0xB0, +0x0A,0xE8,0x0A,0xC9,0xF7,0xD9,0x83,0xC1,0x36,0x8B,0xC1,0x0D,0x80,0x00,0x86,0xC4, +0xA3,0x22,0x13,0x41,0x41,0x89,0x0E,0x20,0x13,0xC3,0xA1,0x84,0x12,0x2B,0xC1,0x72, +0x11,0xA3,0x84,0x12,0xBE,0x22,0x13,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0x20,0x13, +0xF8,0xC3,0xF9,0xC3,0xC3,0x81,0xEF,0x6A,0x13,0x74,0xF9,0x8B,0xC7,0x0D,0x80,0x00, +0x86,0xC4,0xA3,0x68,0x13,0x47,0x47,0x89,0x3E,0x66,0x13,0xC3,0xF7,0x06,0x2A,0x01, +0x01,0x00,0x75,0xE0,0x8B,0x0E,0x66,0x13,0xE3,0x07,0x80,0xF9,0x20,0x77,0xD5,0x49, +0x49,0x33,0xC0,0x8E,0xC0,0x8E,0xD8,0xBF,0x6A,0x13,0x8B,0xF7,0x03,0xF9,0x83,0xC6, +0x34,0x3B,0xFE,0x77,0xC0,0xB0,0x0E,0xE8,0xC8,0xC8,0x85,0xED,0x74,0xB7,0x8A,0x46, +0x4C,0x8A,0xB6,0xB9,0x00,0xFE,0xCE,0x78,0x15,0x88,0xB6,0xB9,0x00,0x8A,0xA6,0xA9, +0x00,0x80,0xCC,0xC0,0xAB,0x84,0xF6,0x74,0x05,0xB0,0x0E,0xE8,0x70,0xC8,0x8A,0xB6, +0xBA,0x00,0xFE,0xCE,0x78,0xCB,0x8A,0x9E,0xA9,0x00,0x8A,0xBE,0xAB,0x00,0x8A,0x56, +0x3F,0x8A,0xF3,0x32,0xF7,0x0A,0xB6,0xAC,0x00,0xC6,0x86,0xAC,0x00,0x00,0x22,0xF2, +0x74,0x4B,0xF6,0xC6,0x08,0x74,0x0F,0xB4,0x02,0xF6,0xC3,0x08,0x75,0x02,0xB4,0x03, +0xAB,0x80,0xE6,0xF7,0x74,0x37,0xF6,0xC6,0x01,0x74,0x0F,0xB4,0x00,0xF6,0xC3,0x01, +0x75,0x02,0xB4,0x01,0xAB,0x80,0xE6,0xFE,0x74,0x23,0xF6,0xC6,0x02,0x74,0x0F,0xB4, +0x04,0xF6,0xC3,0x02,0x75,0x02,0xB4,0x05,0xAB,0x80,0xE6,0xFD,0x74,0x0F,0xF6,0xC6, +0x04,0x74,0x0A,0xB4,0x06,0xF6,0xC3,0x04,0x75,0x02,0xB4,0x07,0xAB,0xC6,0x86,0xBA, +0x00,0x00,0x88,0x9E,0xAB,0x00,0xE9,0x58,0xFF,0x90,0xA1,0x84,0x12,0x2B,0xC1,0x72, +0x11,0xA3,0x84,0x12,0xBE,0x68,0x13,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0x66,0x13, +0xF8,0xC3,0xF9,0xC3,0xA1,0x84,0x12,0x41,0x41,0x2B,0xC1,0x72,0x23,0xA3,0x84,0x12, +0x8B,0xC1,0x48,0x48,0x32,0xE4,0x0C,0x80,0x86,0xC4,0xEF,0x90,0x90,0x90,0x90,0x90, +0xBE,0xDC,0x12,0x49,0x49,0xD1,0xE9,0xF3,0x6F,0x90,0x89,0x0E,0xDA,0x12,0xF8,0xC3, +0xF9,0xC3,0x8A,0xC8,0x8A,0x46,0x4C,0xB4,0x01,0x83,0xEB,0x06,0xEF,0x90,0x90,0x90, +0x90,0x90,0xB8,0x01,0x00,0xEF,0x90,0x90,0x90,0x90,0x90,0x8A,0xC1,0xEF,0x90,0x90, +0x90,0x90,0x90,0xE9,0x97,0x00,0xE9,0xAC,0x00,0x33,0xC0,0x8E,0xD8,0x89,0x1E,0x84, +0x12,0xC3,0x36,0x8B,0x1E,0x84,0x12,0xFB,0x90,0xFA,0xB0,0x0C,0xE8,0xA3,0xC7,0x85, +0xED,0x74,0xE6,0xC5,0x76,0x0C,0x83,0xFB,0x14,0x72,0xDB,0xFB,0x90,0xFA,0xAD,0x85, +0xC0,0x78,0xAF,0x74,0xE2,0x8B,0xFE,0x03,0xF8,0x36,0x8B,0x0E,0x86,0x12,0x3B,0xC1, +0x77,0x02,0x8B,0xC8,0x83,0xEB,0x04,0x3B,0xD9,0x77,0x02,0x8B,0xCB,0x33,0xC0,0x8A, +0x46,0x4C,0xEF,0x90,0x90,0x90,0x90,0x90,0x8B,0xC1,0xEF,0x90,0x90,0x90,0x90,0x90, +0x41,0x80,0xE1,0xFE,0x2B,0xD9,0x51,0xD1,0xE9,0xF3,0x6F,0x90,0x59,0x8B,0xC7,0x40, +0x24,0xFE,0x3B,0xC6,0x74,0x27,0x2B,0xFE,0x4E,0x4E,0x53,0x8B,0x5E,0x10,0x3B,0xF3, +0x72,0x13,0x03,0x1F,0x83,0xC3,0x03,0x80,0xE3,0xFE,0xC7,0x07,0x00,0x00,0x83,0x6E, +0x74,0x02,0x89,0x5E,0x10,0x5B,0x89,0x3C,0x89,0x76,0x0C,0xEB,0x89,0x89,0x76,0x0C, +0x39,0x76,0x10,0x77,0x81,0x72,0x08,0x83,0x3C,0x00,0x74,0x03,0xE9,0x77,0xFF,0xE8, +0x27,0xBE,0xE9,0x62,0xFF,0x36,0x89,0x1E,0x84,0x12,0xB0,0x0C,0xE8,0xCF,0xC6,0x33, +0xC0,0x8E,0xD8,0xC3,0xA1,0x84,0x12,0x3D,0x10,0x00,0x72,0x77,0xBA,0x04,0x01,0x3B, +0x06,0x88,0x12,0x75,0x06,0xC7,0x06,0x7E,0x12,0x00,0x00,0x8B,0x0E,0xDA,0x12,0xE3, +0x0B,0xE8,0xD0,0xFE,0x72,0x57,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0x8B,0x0E,0x20,0x13, +0xE3,0x0B,0xE8,0xA5,0xFD,0x72,0x46,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0x8B,0x0E,0x66, +0x13,0xE3,0x0B,0xE8,0x94,0xFE,0x72,0x35,0xC7,0x06,0x7E,0x12,0xFF,0x7F,0xA1,0x28, +0x01,0xA9,0x01,0x00,0x75,0x03,0xE8,0xF9,0xFE,0x80,0x3E,0x8D,0x12,0x00,0x75,0x1D, +0xA1,0x84,0x12,0x3D,0x20,0x00,0x76,0x15,0x3B,0x06,0x82,0x12,0x76,0x09,0xA1,0x7E, +0x12,0x3B,0x06,0x80,0x12,0x72,0x0C,0x80,0x0E,0x90,0x12,0x80,0xC3,0xB0,0x80,0xFF, +0x16,0x7C,0x12,0xC3,0x80,0x0E,0x90,0x12,0x40,0xC3,0x6A,0x00,0x1F,0xC6,0x06,0x93, +0x12,0x17,0x9C,0x0E,0xE8,0xD1,0xC8,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x20,0x9C, +0x0E,0xE8,0xC4,0xC8,0x6A,0x00,0x1F,0xC6,0x06,0x93,0x12,0x16,0x9C,0x0E,0xE8,0xB7, +0xC8,0x90,0xBA,0x06,0x01,0xEC,0xA8,0x20,0x75,0xCA,0xFB,0x90,0xFA,0xBA,0x04,0x01, +0xED,0x90,0x90,0x90,0x90,0x90,0x3A,0x06,0x94,0x12,0x77,0xBE,0x33,0xDB,0x8A,0xD8, +0xD1,0xE3,0x2E,0x8B,0xAF,0x44,0x00,0xC4,0x7E,0x08,0x85,0xFF,0x74,0xB9,0xF6,0xC4, +0xC0,0x75,0x55,0x32,0xC0,0xC1,0xE0,0x02,0x80,0xE4,0xF0,0x8B,0xF0,0xED,0x90,0x90, +0x90,0x90,0x90,0x85,0xC0,0x74,0xBB,0x8B,0xC8,0x41,0x80,0xE1,0xFE,0x0B,0xC6,0x8B, +0x5E,0x50,0x4B,0x4B,0x2B,0xD9,0x78,0x9C,0xAB,0x8B,0xC1,0x40,0x40,0x01,0x46,0x4E, +0xD1,0xE9,0xF3,0x6D,0x90,0x89,0x5E,0x50,0x89,0x7E,0x08,0x8B,0x46,0x26,0x80,0xE4, +0xEF,0x89,0x46,0x26,0xF6,0xC4,0x01,0x75,0x0C,0xF7,0x46,0x48,0x0C,0x00,0x75,0x05, +0xB0,0x02,0xE8,0x99,0xC5,0xE9,0x7A,0xFF,0x86,0xC4,0x8B,0xC8,0x83,0xE1,0x3F,0x41, +0x80,0xE1,0xFE,0xE3,0x0A,0x3C,0x80,0x72,0x09,0x24,0x3F,0xB4,0xF0,0xEB,0xB0,0xE9, +0x60,0xFF,0x25,0x3F,0x00,0x33,0xFF,0x8E,0xC7,0xBF,0x96,0x12,0x8B,0xF7,0xD1,0xE9, +0xF3,0x6D,0x90,0x8B,0xC8,0xE8,0x48,0xED,0xE9,0x47,0xFF,0x90,0x6A,0x00,0x1F,0xC6, +0x06,0x93,0x12,0x1B,0x9C,0x0E,0xE8,0xEF,0xC7,0x90,0x60,0x1E,0x06,0x33,0xC0,0x8E, +0xD8,0x8E,0xC0,0xBA,0x06,0x01,0xEC,0xA8,0x04,0x74,0xE1,0xB0,0x06,0xEE,0xEC,0xA2, +0x8C,0x12,0xA8,0x40,0x74,0x11,0xA1,0x88,0x12,0xA3,0x84,0x12,0xC6,0x06,0x8D,0x12, +0x00,0xE8,0x60,0xFE,0xA0,0x8C,0x12,0xA8,0x80,0x74,0x03,0xE8,0x04,0xFF,0xB8,0x00, +0x80,0xBA,0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0x6A,0x00,0x1F,0xC6,0x06,0x93, +0x12,0x1B,0x9C,0x0E,0xE8,0xA1,0xC7,0x90,0x60,0x1E,0x06,0x33,0xC0,0x8E,0xD8,0x8E, +0xC0,0xBA,0x06,0x01,0xEC,0xA8,0x04,0x74,0xE1,0xBA,0x08,0x01,0xEC,0xA2,0x8C,0x12, +0xA8,0x40,0x74,0x11,0xA1,0x88,0x12,0xA3,0x84,0x12,0xC6,0x06,0x8D,0x12,0x00,0xE8, +0x12,0xFE,0xA0,0x8C,0x12,0xA8,0x80,0x74,0x03,0xE8,0xB6,0xFE,0xB8,0x00,0x80,0xBA, +0x22,0xFF,0xEF,0x07,0x1F,0x61,0xCF,0x90,0xEE,0x86,0xE0,0xEE,0x86,0xE0,0xEC,0x86, +0xE0,0xEC,0x86,0xE0,0x80,0xE1,0xFE,0xF3,0x6C,0x90,0x80,0xE1,0xFE,0xF3,0x6E,0x90, +0x05,0x00,0x57,0x47,0x8A,0x4B,0x05,0x00,0x57,0x48,0x8A,0x4B,0x05,0x00,0x85,0x48, +0x8A,0x4B,0x05,0x00,0x17,0x49,0x8A,0x4B,0x06,0x00,0x7A,0x48,0x78,0x4B,0x06,0x00, +0x9C,0x48,0x78,0x4B,0x06,0x00,0xA5,0x48,0x78,0x4B,0x06,0x00,0xAD,0x48,0x78,0x4B, +0x06,0x00,0x02,0x49,0x78,0x4B,0x06,0x00,0x0A,0x49,0x78,0x4B,0x06,0x00,0x30,0x4A, +0x7E,0x4B,0x06,0x00,0x5D,0x4A,0x7E,0x4B,0x05,0x00,0x80,0x4A,0x84,0x4B,0x05,0x00, +0xCE,0x4A,0x84,0x4B,0x00,0x00,0x1E,0x06,0x83,0x3E,0x44,0x12,0x00,0x74,0x09,0xA0, +0x06,0x01,0x24,0x30,0x3C,0x30,0x74,0x1A,0x8C,0xC8,0x8E,0xD8,0x8E,0xC0,0xBB,0x90, +0x4B,0x8B,0x0F,0xE3,0x0D,0x8B,0x7F,0x02,0x8B,0x77,0x04,0xF3,0xA4,0x83,0xC3,0x06, +0xEB,0xEF,0x07,0x1F,0xC3,0x90,0x33,0xC0,0xA3,0x3E,0x01,0xB9,0x0C,0x01,0xBE,0x40, +0x01,0x8B,0xFE,0x81,0xC6,0xB4,0x0F,0x89,0x04,0x8B,0xC6,0x2B,0xF1,0x3B,0xC7,0x77, +0xF6,0xA3,0x3C,0x01,0xC3,0x90,0x1E,0x06,0x60,0x36,0x8B,0x2E,0x3E,0x01,0x8B,0x5E, +0x00,0x3B,0xEB,0x74,0x2B,0x8B,0x76,0x02,0x89,0x1C,0x89,0x77,0x02,0x36,0xA1,0x3C, +0x01,0x89,0x46,0x00,0x36,0x89,0x2E,0x3C,0x01,0x8B,0xEB,0xFF,0x4E,0x06,0x74,0x08, +0x8B,0x6E,0x00,0xFF,0x4E,0x06,0x75,0xF8,0x36,0x89,0x2E,0x3E,0x01,0x8B,0x66,0x04, +0x61,0x07,0x1F,0xC3,0x1E,0x06,0x60,0x36,0x8B,0x2E,0x3E,0x01,0x98,0x89,0x46,0x06, +0x89,0x66,0x04,0x3B,0x6E,0x00,0x74,0x10,0x8B,0x6E,0x00,0xFF,0x4E,0x06,0x75,0xF8, +0x36,0x89,0x2E,0x3E,0x01,0x8B,0x66,0x04,0x61,0x07,0x1F,0xC3,0xC3,0x90,0x1E,0x06, +0x60,0x9C,0xFA,0x33,0xED,0x8E,0xDD,0x8B,0x2E,0x3C,0x01,0x85,0xED,0x74,0x3D,0x8B, +0x4E,0x00,0x89,0x0E,0x3C,0x01,0x8B,0xCC,0x8D,0xA6,0x0A,0x01,0x56,0x1E,0x06,0x60, +0x89,0x66,0x04,0xC7,0x46,0x08,0x0F,0x1A,0xC7,0x46,0x06,0x01,0x00,0x8B,0x1E,0x3E, +0x01,0x85,0xDB,0x74,0x1D,0x8B,0xC5,0x87,0x07,0x89,0x46,0x00,0x89,0x5E,0x02,0x8B, +0xD8,0x89,0x6F,0x02,0x8B,0xE1,0x9D,0x61,0x07,0x1F,0xF8,0xC3,0x9D,0x61,0x07,0x1F, +0xF9,0xC3,0x89,0x2E,0x3E,0x01,0x89,0x6E,0x00,0x89,0x6E,0x02,0x87,0xE1,0x9D,0x8B, +0xE1,0xEB,0xE4,0x00,0x0D,0x0A,0x54,0x65,0x72,0x6D,0x69,0x6E,0x61,0x6C,0x73,0x20, +0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x65,0x64,0x3A,0x0D,0x0A,0x31,0x29,0x20,0x41, +0x4E,0x53,0x49,0x20,0x63,0x6F,0x6D,0x70,0x61,0x74,0x69,0x62,0x6C,0x65,0x0D,0x0A, +0x32,0x29,0x20,0x57,0x79,0x73,0x65,0x20,0x33,0x30,0x0D,0x0A,0x50,0x6C,0x65,0x61, +0x73,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,0x3A,0x20,0x00,0x0D,0x0A,0x63,0x6F, +0x64,0x65,0x20,0x73,0x65,0x67,0x6D,0x65,0x6E,0x74,0x3D,0x00,0x0D,0x0A,0x4D,0x6F, +0x6E,0x69,0x74,0x6F,0x72,0x20,0x76,0x32,0x2E,0x35,0x0A,0x0D,0x0A,0x3E,0x00,0x0D, +0x0A,0x50,0x61,0x72,0x64,0x6F,0x6E,0x3F,0x00,0x0D,0x0A,0x4E,0x6F,0x20,0x61,0x64, +0x64,0x72,0x65,0x73,0x73,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x00, +0x0D,0x0A,0x3A,0x00,0x0D,0x0A,0x00,0x4C,0x6F,0x63,0x3D,0x00,0x0D,0x0A,0x46,0x41, +0x54,0x41,0x4C,0x20,0x45,0x52,0x52,0x4F,0x52,0x3D,0x00,0x0D,0x0A,0x4D,0x6F,0x6E, +0x69,0x74,0x6F,0x72,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x73,0x3A,0x2D,0x0D, +0x0A,0x20,0x20,0x20,0x44,0x2C,0x64,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78, +0x78,0x78,0x78,0x5D,0x20,0x2D,0x20,0x64,0x75,0x6D,0x70,0x20,0x6D,0x65,0x6D,0x6F, +0x72,0x79,0x0D,0x0A,0x20,0x20,0x20,0x4C,0x2C,0x6C,0x5B,0x5B,0x78,0x78,0x78,0x78, +0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20,0x2D,0x20,0x64,0x75,0x6D,0x70,0x20,0x73, +0x69,0x6E,0x67,0x6C,0x65,0x20,0x6C,0x69,0x6E,0x65,0x0D,0x0A,0x20,0x20,0x20,0x45, +0x2C,0x65,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20, +0x2D,0x20,0x65,0x64,0x69,0x74,0x20,0x6D,0x65,0x6D,0x6F,0x72,0x79,0x0D,0x0A,0x20, +0x20,0x20,0x46,0x2C,0x66,0x5B,0x5B,0x78,0x78,0x78,0x78,0x20,0x5D,0x78,0x78,0x78, +0x78,0x5D,0x20,0x2D,0x20,0x66,0x69,0x6C,0x6C,0x20,0x6D,0x65,0x6D,0x6F,0x72,0x79, +0x20,0x70,0x61,0x72,0x61,0x67,0x72,0x61,0x70,0x68,0x73,0x0D,0x0A,0x20,0x20,0x20, +0x49,0x5B,0x78,0x78,0x78,0x78,0x5D,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x2D,0x20,0x77,0x6F,0x72,0x64,0x20,0x69,0x6E,0x70,0x75,0x74,0x20,0x66,0x72, +0x6F,0x6D,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x69,0x5B,0x78,0x78, +0x78,0x78,0x5D,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62, +0x79,0x74,0x65,0x20,0x69,0x6E,0x70,0x75,0x74,0x20,0x66,0x72,0x6F,0x6D,0x20,0x70, +0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x4F,0x78,0x78,0x78,0x78,0x20,0x78,0x78, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x6F,0x75,0x74,0x70,0x75, +0x74,0x20,0x77,0x6F,0x72,0x64,0x20,0x74,0x6F,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A, +0x20,0x20,0x20,0x6F,0x78,0x78,0x78,0x78,0x20,0x78,0x78,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x2D,0x20,0x6F,0x75,0x74,0x70,0x75,0x74,0x20,0x62,0x79,0x74, +0x65,0x20,0x74,0x6F,0x20,0x70,0x6F,0x72,0x74,0x0D,0x0A,0x20,0x20,0x20,0x47,0x5B, +0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D,0x20,0x20,0x20,0x2D, +0x20,0x67,0x6F,0x74,0x6F,0x20,0x61,0x64,0x64,0x72,0x65,0x73,0x73,0x0D,0x0A,0x20, +0x20,0x20,0x57,0x5B,0x5B,0x78,0x78,0x78,0x78,0x3A,0x5D,0x78,0x78,0x78,0x78,0x5D, +0x20,0x20,0x20,0x2D,0x20,0x77,0x61,0x74,0x63,0x68,0x20,0x61,0x20,0x77,0x6F,0x72, +0x64,0x0D,0x0A,0x20,0x20,0x20,0x43,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x69,0x6E,0x74,0x65,0x72,0x72,0x75, +0x70,0x74,0x73,0x20,0x6F,0x66,0x66,0x0D,0x0A,0x20,0x20,0x20,0x53,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x69, +0x6E,0x74,0x65,0x72,0x72,0x75,0x70,0x74,0x73,0x20,0x6F,0x6E,0x0D,0x0A,0x20,0x20, +0x20,0x73,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x2D,0x20,0x73,0x69,0x6E,0x67,0x6C,0x65,0x20,0x73,0x74,0x65,0x70,0x0D, +0x0A,0x20,0x20,0x20,0x42,0x78,0x78,0x78,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62,0x72,0x65,0x61,0x6B,0x70,0x6F,0x69,0x6E, +0x74,0x20,0x73,0x65,0x74,0x0D,0x0A,0x20,0x20,0x20,0x62,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x62,0x72,0x65, +0x61,0x6B,0x70,0x6F,0x69,0x6E,0x74,0x20,0x63,0x6C,0x65,0x61,0x72,0x0D,0x0A,0x20, +0x20,0x20,0x52,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x2D,0x20,0x72,0x65,0x73,0x74,0x61,0x72,0x74,0x20,0x62,0x72,0x65, +0x61,0x6B,0x70,0x6F,0x69,0x6E,0x74,0x0D,0x0A,0x20,0x20,0x20,0x72,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x72, +0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x73,0x20,0x61,0x74,0x20,0x62,0x72,0x6B,0x70, +0x74,0x0D,0x0A,0x20,0x20,0x20,0x58,0x2C,0x78,0x20,0x6E,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2D,0x20,0x65,0x78,0x61,0x6D,0x69,0x6E,0x65, +0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x6E,0x0D,0x0A,0x20,0x20,0x20,0x48, +0x2C,0x3F,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +0x2D,0x20,0x74,0x68,0x69,0x73,0x20,0x6D,0x65,0x73,0x73,0x61,0x67,0x65,0x00,0x1B, +0x5B,0x32,0x4A,0x1B,0x5B,0x31,0x3B,0x31,0x48,0x41,0x4E,0x53,0x49,0x20,0x54,0x65, +0x72,0x6D,0x69,0x6E,0x61,0x6C,0x0D,0x0A,0x0A,0x00,0x1B,0x5B,0x4B,0x00,0x1B,0x5B, +0x4A,0x00,0x1B,0x5B,0x32,0x4A,0x1B,0x5B,0x31,0x3B,0x31,0x48,0x00,0x1B,0x5B,0x44, +0x20,0x1B,0x5B,0x44,0x00,0x1B,0x5B,0x31,0x3B,0x37,0x32,0x48,0x00,0x1B,0x5B,0x00, +0x3B,0x00,0x48,0x00,0x1B,0x5B,0x73,0x00,0x1B,0x5B,0x75,0x00,0x1B,0x7A,0x2B,0x0B, +0x7F,0x1B,0x7A,0x2E,0x0C,0x7F,0x1B,0x7A,0x2D,0x08,0x7F,0x1B,0x7A,0x2C,0x0A,0x7F, +0x1B,0x7A,0x22,0x08,0x7F,0x1A,0x57,0x79,0x73,0x65,0x20,0x33,0x30,0x20,0x54,0x65, +0x72,0x6D,0x69,0x6E,0x61,0x6C,0x0D,0x0A,0x00,0x1B,0x54,0x00,0x1B,0x59,0x00,0x1A, +0x00,0x1E,0x00,0x08,0x20,0x08,0x00,0x00,0x1B,0x3D,0x00,0x00,0x00,0x1B,0x46,0x00, +0x0D,0x00,0x3F,0x44,0x64,0x45,0x65,0x46,0x66,0x47,0x67,0x48,0x68,0x49,0x69,0x4F, +0x6F,0x43,0x63,0x53,0x73,0x42,0x62,0x52,0x72,0x57,0x77,0x58,0x78,0x4C,0x6C,0x1E, +0x60,0xB6,0x57,0xB6,0x57,0x32,0x58,0x32,0x58,0xB8,0x59,0xB8,0x59,0x96,0x59,0x96, +0x59,0x1E,0x60,0x1E,0x60,0x4E,0x57,0x2A,0x57,0x08,0x57,0xE8,0x56,0x72,0x57,0x72, +0x57,0x7A,0x57,0x2A,0x5F,0xEE,0x5E,0x3A,0x5F,0x15,0x5F,0x22,0x5F,0x82,0x57,0x82, +0x57,0xE0,0x59,0xE0,0x59,0xBE,0x57,0xBE,0x57,0x6A,0x61,0x7A,0x61,0xA2,0x61,0xAE, +0x61,0xBA,0x61,0xD8,0x61,0xE4,0x61,0x04,0x62,0xDA,0x56,0x2C,0x62,0x3A,0x62,0x42, +0x59,0x20,0x20,0x66,0x6C,0x61,0x67,0x73,0x3D,0x00,0x20,0x20,0x61,0x78,0x3D,0x00, +0x20,0x20,0x62,0x78,0x3D,0x00,0x20,0x20,0x63,0x78,0x3D,0x00,0x20,0x20,0x64,0x78, +0x3D,0x00,0x20,0x20,0x63,0x73,0x3D,0x00,0x20,0x20,0x64,0x73,0x3D,0x00,0x20,0x20, +0x65,0x73,0x3D,0x00,0x20,0x20,0x73,0x73,0x3D,0x00,0x20,0x20,0x64,0x69,0x3D,0x00, +0x20,0x20,0x73,0x69,0x3D,0x00,0x20,0x20,0x62,0x70,0x3D,0x00,0x20,0x20,0x73,0x70, +0x3D,0x00,0x20,0x20,0x69,0x70,0x3D,0x00,0x20,0x63,0x68,0x61,0x6E,0x65,0x6C,0x3D, +0x00,0x20,0x20,0x20,0x20,0x73,0x65,0x67,0x3D,0x00,0x20,0x74,0x69,0x5F,0x73,0x74, +0x72,0x3D,0x00,0x20,0x74,0x69,0x5F,0x74,0x6F,0x73,0x3D,0x00,0x20,0x74,0x69,0x5F, +0x6D,0x61,0x78,0x3D,0x00,0x20,0x74,0x69,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x74, +0x69,0x5F,0x73,0x69,0x7A,0x3D,0x00,0x20,0x74,0x69,0x5F,0x73,0x74,0x66,0x3D,0x00, +0x20,0x74,0x69,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20,0x74,0x69,0x5F,0x66,0x6C,0x67, +0x3D,0x00,0x20,0x74,0x69,0x5F,0x74,0x6F,0x74,0x3D,0x00,0x20,0x72,0x69,0x5F,0x70, +0x63,0x6E,0x3D,0x00,0x20,0x72,0x69,0x5F,0x73,0x74,0x72,0x3D,0x00,0x20,0x72,0x69, +0x5F,0x73,0x74,0x66,0x3D,0x00,0x20,0x72,0x69,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20, +0x72,0x69,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x73,0x69,0x7A,0x3D, +0x00,0x20,0x72,0x69,0x5F,0x74,0x6F,0x74,0x3D,0x00,0x20,0x72,0x69,0x5F,0x6D,0x69, +0x6E,0x3D,0x00,0x20,0x72,0x69,0x5F,0x66,0x6C,0x67,0x3D,0x00,0x20,0x72,0x69,0x5F, +0x74,0x6F,0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x74,0x68,0x72,0x3D,0x00,0x20,0x74, +0x68,0x5F,0x73,0x74,0x66,0x3D,0x00,0x20,0x74,0x68,0x5F,0x73,0x74,0x72,0x3D,0x00, +0x20,0x74,0x68,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x74,0x68,0x5F,0x73,0x69,0x7A, +0x3D,0x00,0x20,0x74,0x68,0x5F,0x74,0x72,0x67,0x3D,0x00,0x20,0x74,0x68,0x5F,0x66, +0x6C,0x67,0x3D,0x00,0x20,0x74,0x68,0x5F,0x63,0x6E,0x74,0x3D,0x00,0x20,0x72,0x68, +0x5F,0x73,0x74,0x72,0x3D,0x00,0x20,0x72,0x68,0x5F,0x73,0x74,0x66,0x3D,0x00,0x20, +0x72,0x68,0x5F,0x62,0x61,0x73,0x3D,0x00,0x20,0x72,0x68,0x5F,0x73,0x69,0x7A,0x3D, +0x00,0x20,0x72,0x68,0x5F,0x73,0x70,0x61,0x3D,0x00,0x20,0x72,0x68,0x5F,0x61,0x73, +0x6F,0x3D,0x00,0x20,0x72,0x68,0x5F,0x72,0x6F,0x6F,0x3D,0x00,0x20,0x72,0x68,0x5F, +0x66,0x6C,0x67,0x3D,0x00,0x20,0x6D,0x5F,0x63,0x61,0x72,0x65,0x3D,0x00,0x20,0x70, +0x74,0x5F,0x66,0x6C,0x6F,0x3D,0x00,0x20,0x61,0x73,0x5F,0x66,0x6C,0x6F,0x3D,0x00, +0x20,0x72,0x6D,0x5F,0x66,0x6C,0x6F,0x3D,0x00,0x20,0x20,0x20,0x71,0x5F,0x69,0x6E, +0x3D,0x00,0x20,0x20,0x71,0x5F,0x6F,0x75,0x74,0x3D,0x00,0x20,0x71,0x5F,0x64,0x72, +0x61,0x6E,0x3D,0x00,0x20,0x20,0x71,0x5F,0x74,0x69,0x6D,0x3D,0x00,0x20,0x20,0x20, +0x71,0x5F,0x66,0x63,0x3D,0x00,0x20,0x71,0x5F,0x73,0x74,0x61,0x74,0x3D,0x00,0x20, +0x71,0x5F,0x64,0x61,0x74,0x61,0x3D,0x00,0x20,0x71,0x5F,0x6D,0x6F,0x64,0x6D,0x3D, +0x00,0x20,0x68,0x61,0x6E,0x64,0x5F,0x6F,0x3D,0x00,0x20,0x68,0x61,0x6E,0x64,0x5F, +0x62,0x3D,0x00,0x20,0x68,0x61,0x6E,0x64,0x5F,0x65,0x3D,0x00,0x20,0x68,0x61,0x6E, +0x64,0x5F,0x69,0x3D,0x00,0x20,0x20,0x6F,0x70,0x6F,0x73,0x74,0x3D,0x00,0x20,0x20, +0x74,0x69,0x6D,0x65,0x6F,0x3D,0x00,0x20,0x63,0x75,0x73,0x74,0x6D,0x31,0x3D,0x00, +0x20,0x63,0x75,0x73,0x74,0x6D,0x32,0x3D,0x00,0x20,0x63,0x75,0x73,0x74,0x6D,0x64, +0x3D,0x00,0x20,0x74,0x78,0x72,0x61,0x74,0x65,0x3D,0x00,0x20,0x72,0x78,0x72,0x61, +0x74,0x65,0x3D,0x00,0x20,0x20,0x63,0x5F,0x6D,0x61,0x70,0x3D,0x00,0x20,0x63,0x5F, +0x61,0x64,0x64,0x72,0x3D,0x00,0x20,0x63,0x5F,0x61,0x69,0x73,0x72,0x3D,0x00,0x20, +0x63,0x5F,0x78,0x74,0x61,0x67,0x3D,0x00,0x20,0x63,0x5F,0x64,0x65,0x66,0x72,0x3D, +0x00,0x20,0x63,0x5F,0x66,0x6C,0x73,0x68,0x3D,0x00,0x20,0x74,0x78,0x6D,0x61,0x78, +0x73,0x3D,0x00,0x20,0x72,0x69,0x5F,0x65,0x6D,0x73,0x3D,0x00,0x20,0x20,0x63,0x5F, +0x6C,0x73,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x69,0x65,0x72,0x3D,0x00,0x20,0x20, +0x63,0x5F,0x66,0x63,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x6D,0x63,0x72,0x3D,0x00, +0x20,0x20,0x63,0x5F,0x6C,0x63,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x64,0x73,0x73, +0x3D,0x00,0x20,0x63,0x5F,0x64,0x73,0x73,0x69,0x3D,0x00,0x20,0x63,0x5F,0x64,0x73, +0x73,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x69,0x73,0x72,0x3D,0x00,0x20,0x20,0x63, +0x5F,0x63,0x61,0x72,0x3D,0x00,0x20,0x20,0x63,0x5F,0x65,0x66,0x72,0x3D,0x00,0x20, +0x63,0x5F,0x65,0x72,0x73,0x74,0x3D,0x00,0x20,0x63,0x5F,0x65,0x63,0x6E,0x74,0x3D, +0x00,0x20,0x63,0x5F,0x62,0x72,0x6B,0x63,0x3D,0x00,0x20,0x63,0x5F,0x62,0x6F,0x6B, +0x63,0x3D,0x00,0x20,0x63,0x5F,0x72,0x65,0x70,0x6C,0x3D,0x00,0x20,0x63,0x5F,0x63, +0x63,0x73,0x72,0x3D,0x00,0x20,0x63,0x5F,0x73,0x74,0x74,0x31,0x3D,0x00,0x20,0x63, +0x5F,0x73,0x74,0x74,0x32,0x3D,0x00,0x2B,0xC0,0x8E,0xD8,0x8E,0xC0,0xE8,0xC2,0x00, +0xE8,0xE5,0x00,0xFA,0xBF,0x84,0x00,0xC7,0x05,0xBE,0x56,0x8C,0x4D,0x02,0xBF,0x0C, +0x00,0xC7,0x05,0x50,0x5E,0x8C,0x4D,0x02,0xBF,0x04,0x00,0xC7,0x05,0x9C,0x5E,0x8C, +0x4D,0x02,0xE8,0xF1,0x00,0x90,0xE8,0x49,0x01,0xE8,0x16,0x00,0xF4,0x90,0xE8,0xE5, +0x00,0xBE,0x9C,0x4D,0xE8,0x09,0x0C,0xA0,0x93,0x12,0xE8,0x5D,0x0C,0xE8,0xC2,0x09, +0xEB,0xE4,0xE8,0xD5,0x0C,0xE8,0xC4,0x0C,0x0A,0xC0,0x74,0xF6,0x8B,0x1E,0xF8,0x79, +0x3C,0x0D,0x74,0x2E,0x3C,0x08,0x74,0x17,0x3C,0x7F,0x74,0x13,0x83,0xFB,0x20,0x7F, +0xE1,0x88,0x87,0xD6,0x79,0x43,0x89,0x1E,0xF8,0x79,0xE8,0x77,0x0C,0xEB,0xD3,0x0B, +0xDB,0x74,0xCF,0x4B,0x89,0x1E,0xF8,0x79,0x8B,0x36,0x16,0x7A,0xE8,0xC1,0x0B,0xEB, +0xC1,0x90,0xE8,0x02,0x00,0xEB,0xBB,0xC6,0x87,0xD6,0x79,0x00,0x0B,0xDB,0x74,0x1E, +0xA0,0xD6,0x79,0xBF,0x42,0x51,0xB9,0x1D,0x00,0x8B,0xD9,0x06,0x0E,0x07,0xF2,0xAE, +0x07,0x75,0x17,0x41,0x2B,0xD9,0xD1,0xE3,0x2E,0xFF,0x97,0x5F,0x51,0x90,0x33,0xC0, +0xA3,0xF8,0x79,0xBE,0x6B,0x4D,0xE8,0x87,0x0B,0xC3,0xBE,0x6F,0x4D,0xE8,0x80,0x0B, +0xEB,0xEC,0xBA,0x00,0x02,0xB0,0x93,0xEE,0xB0,0x55,0xEE,0xBA,0x10,0x02,0xB0,0x93, +0xEE,0xB0,0xAA,0xEE,0xBA,0x00,0x02,0xEC,0x3C,0x55,0x75,0x08,0xBA,0x10,0x02,0xEC, +0x3C,0xAA,0x74,0x03,0xE8,0x2F,0xF6,0xC3,0xBA,0x04,0x02,0xB0,0x1A,0xEE,0xB0,0x20, +0xEE,0xB0,0x30,0xEE,0xB0,0x40,0xEE,0xB0,0x80,0xEE,0xBA,0x00,0x02,0xB0,0x13,0xEE, +0xB0,0x07,0xEE,0xBA,0x08,0x02,0xB0,0x80,0xEE,0xBA,0x02,0x02,0xB0,0xBB,0xEE,0xBA, +0x04,0x02,0xB0,0x05,0xEE,0xC3,0xC6,0x06,0xCA,0x13,0x01,0xC7,0x06,0xF8,0x79,0x00, +0x00,0xC6,0x06,0xF6,0x79,0x01,0xC7,0x06,0xD0,0x79,0x00,0x00,0xC7,0x06,0xD2,0x79, +0x00,0x00,0xC7,0x06,0xD4,0x79,0x00,0x00,0xC7,0x06,0xFA,0x79,0x00,0x00,0xC7,0x06, +0xFC,0x79,0x00,0x00,0xC7,0x06,0xFE,0x79,0x00,0x00,0xC7,0x06,0x00,0x7A,0x00,0x00, +0xC7,0x06,0x02,0x7A,0xB0,0x59,0x8C,0x0E,0x04,0x7A,0xC7,0x06,0x06,0x7A,0x00,0x00, +0xC7,0x06,0x27,0x7A,0x00,0x00,0xC6,0x06,0x29,0x7A,0x00,0xC6,0x06,0x2A,0x7A,0x00, +0xC3,0x90,0xBE,0x04,0x4D,0xE8,0xC8,0x0A,0xE8,0x3F,0x00,0x2C,0x31,0x3C,0x01,0x77, +0xF7,0xE8,0x81,0x09,0x8B,0x36,0x0C,0x7A,0xE8,0xB5,0x0A,0xBE,0x4C,0x4D,0xE8,0xAF, +0x0A,0x0E,0x58,0xE8,0xF8,0x0A,0xBE,0x5C,0x4D,0xE8,0xA4,0x0A,0xC3,0x90,0x60,0xD1, +0xE3,0x83,0xFB,0x18,0x73,0x11,0x1E,0xBA,0x00,0x00,0x8E,0xDA,0x2E,0xFF,0x97,0x99, +0x51,0x8B,0xEC,0x89,0x46,0x10,0x1F,0x61,0xCF,0x90,0xE8,0x4F,0x0B,0x0A,0xC0,0x75, +0x05,0xE8,0x56,0x0B,0xEB,0xF4,0xC3,0x90,0x83,0x3E,0xF8,0x79,0x01,0x74,0x16,0xBE, +0xD7,0x79,0xE8,0x31,0x0A,0x8B,0xD0,0xAC,0x3C,0x2C,0x74,0x04,0x3C,0x20,0x75,0x05, +0xE8,0x23,0x0A,0xEE,0xC3,0xE9,0xD2,0xFE,0x83,0x3E,0xF8,0x79,0x01,0x74,0xF6,0xBE, +0xD7,0x79,0xE8,0x11,0x0A,0x8B,0xD0,0xAC,0x3C,0x2C,0x74,0x08,0x3C,0x20,0x74,0x04, +0xE9,0xB7,0xFE,0x90,0xE8,0xFF,0x09,0xEF,0xC3,0x90,0x8B,0x16,0x06,0x7A,0x83,0x3E, +0xF8,0x79,0x01,0x74,0x0B,0xBE,0xD7,0x79,0xE8,0xEB,0x09,0x8B,0xD0,0xA3,0x06,0x7A, +0xB0,0x20,0xE8,0x57,0x0B,0x8B,0x16,0x06,0x7A,0xEC,0xE8,0x6F,0x0B,0xC3,0x8B,0x16, +0x06,0x7A,0x83,0x3E,0xF8,0x79,0x01,0x74,0x0B,0xBE,0xD7,0x79,0xE8,0xC7,0x09,0x8B, +0xD0,0xA3,0x06,0x7A,0xB0,0x20,0xE8,0x33,0x0B,0x8B,0x16,0x06,0x7A,0xED,0xE8,0x67, +0x0B,0xC3,0xFA,0xC6,0x06,0xF6,0x79,0x00,0xC3,0x90,0xC6,0x06,0xF6,0x79,0x01,0xFB, +0xC3,0x90,0x06,0xE8,0x58,0x09,0xB0,0x20,0xE8,0x11,0x0B,0x26,0x8B,0x05,0xE8,0x47, +0x0B,0xB0,0x08,0xE8,0x06,0x0B,0xE8,0x03,0x0B,0xE8,0x00,0x0B,0xE8,0xFD,0x0A,0xB8, +0x01,0x00,0xE8,0xCF,0xF4,0xBA,0x02,0x02,0xEC,0x24,0x01,0x75,0x02,0xEB,0xDC,0xBA, +0x06,0x02,0xEC,0x07,0xC3,0x90,0xC7,0x06,0x08,0x7A,0x10,0x00,0xEB,0x06,0xC7,0x06, +0x08,0x7A,0x01,0x00,0x06,0x8E,0x06,0xFC,0x79,0x8B,0x3E,0xFA,0x79,0xE8,0x0E,0x09, +0xE8,0x0B,0x00,0x89,0x3E,0xFA,0x79,0x8C,0x06,0xFC,0x79,0x07,0xC3,0x90,0xBE,0x94, +0x4D,0xE8,0x7C,0x09,0x8B,0x16,0x08,0x7A,0x52,0xE8,0x2A,0x09,0xE8,0x0F,0x0A,0xE8, +0x0C,0x0A,0x33,0xDB,0xB9,0x10,0x00,0x90,0x26,0x8A,0x01,0xE8,0xBC,0x09,0xE8,0xFD, +0x09,0x43,0xE2,0xF4,0xE8,0xF7,0x09,0xE8,0xF4,0x09,0x33,0xDB,0xB9,0x10,0x00,0x90, +0x26,0x8A,0x01,0x3C,0x20,0x72,0x05,0x3C,0x7E,0x76,0x03,0x90,0xB0,0x2E,0xE8,0xE3, +0x09,0x43,0xE2,0xEC,0xBE,0x94,0x4D,0xE8,0x36,0x09,0x83,0xC7,0x10,0x5A,0x4A,0x75, +0xB7,0xC3,0x06,0x8E,0x06,0x00,0x7A,0x8B,0x3E,0xFE,0x79,0xE8,0xA0,0x08,0x89,0x3E, +0xFE,0x79,0x8C,0x06,0x00,0x7A,0x57,0x8B,0x36,0x0E,0x7A,0xE8,0x12,0x09,0xC7,0x06, +0x08,0x7A,0x10,0x00,0xBA,0x00,0x02,0xE8,0xE8,0x00,0xE8,0x81,0xFF,0x5F,0xBA,0x00, +0x00,0xE8,0xDE,0x00,0xBE,0x97,0x4D,0xE8,0xF6,0x08,0x8C,0xC0,0xE8,0x3F,0x09,0xB0, +0x3A,0xE8,0x90,0x09,0x8B,0xC7,0xE8,0x35,0x09,0xE8,0x7E,0x08,0xE8,0xC3,0x00,0x90, +0xE8,0xB7,0x09,0xE8,0xA6,0x09,0x0A,0xC0,0x74,0xF6,0x3C,0x0B,0x75,0x06,0x83,0xEF, +0x10,0xEB,0x19,0x90,0x3C,0x0A,0x75,0x06,0x83,0xC7,0x10,0xEB,0x0F,0x90,0x3C,0x0C, +0x75,0x04,0x47,0xEB,0x07,0x90,0x3C,0x08,0x75,0x24,0x4F,0x90,0x8B,0x36,0xFE,0x79, +0x8B,0xC7,0x2B,0xC6,0x3D,0x00,0x01,0x72,0xA5,0x3D,0x10,0x01,0x72,0x04,0x83,0xEE, +0x20,0x90,0x83,0xC6,0x10,0x89,0x36,0xFE,0x79,0x57,0x8B,0xFE,0xEB,0x80,0x3C,0x2E, +0x75,0x08,0xBA,0x01,0x13,0xE8,0x6A,0x00,0x07,0xC3,0xC6,0x06,0x0A,0x7A,0x02,0x32, +0xC9,0x90,0x3C,0x30,0x72,0x4C,0x3C,0x39,0x76,0x0C,0x24,0x5F,0x3C,0x41,0x72,0x42, +0x3C,0x46,0x77,0x3E,0x2C,0x07,0x2C,0x30,0x50,0xE8,0xCC,0x08,0x58,0x02,0xC8,0xFE, +0x0E,0x0A,0x7A,0x74,0x0F,0xC0,0xE1,0x04,0xE8,0x2F,0x09,0xE8,0x1E,0x09,0x0A,0xC0, +0x74,0xF6,0xEB,0xCE,0x26,0x88,0x0D,0xE8,0xE0,0x07,0x8A,0xD0,0xE8,0x23,0x00,0x8A, +0xC1,0x3C,0x20,0x72,0x05,0x3C,0x7E,0x76,0x03,0x90,0xB0,0x2E,0xE8,0xD5,0x08,0xE9, +0x70,0xFF,0xE8,0xC5,0x07,0xE8,0x0A,0x00,0x26,0x8A,0x05,0xE8,0x7C,0x08,0xE9,0x1D, +0xFF,0x90,0xF6,0x06,0x26,0x7A,0x02,0x75,0x02,0x86,0xF2,0x52,0x8B,0x36,0x1A,0x7A, +0xE8,0x0D,0x08,0x5A,0x52,0x8A,0xC6,0x02,0x06,0x24,0x7A,0xF6,0x06,0x26,0x7A,0x01, +0x75,0x06,0xE8,0x9F,0x08,0xEB,0x0D,0x90,0x32,0xE4,0xE8,0x0D,0x08,0x8B,0x36,0x1C, +0x7A,0xE8,0xEC,0x07,0x5A,0x8A,0xC2,0x02,0x06,0x25,0x7A,0xF6,0x06,0x26,0x7A,0x01, +0x75,0x06,0xE8,0x7F,0x08,0xEB,0x06,0x90,0x32,0xE4,0xE8,0xED,0x07,0x8B,0x36,0x1E, +0x7A,0xE8,0xCC,0x07,0xC3,0x90,0x06,0x8E,0x06,0x04,0x7A,0x8B,0x3E,0x02,0x7A,0xE8, +0x3C,0x07,0x89,0x3E,0x02,0x7A,0x8C,0x06,0x04,0x7A,0x07,0xFF,0x1E,0x02,0x7A,0xC3, +0xBE,0x79,0x4D,0xE8,0xAA,0x07,0xCB,0x90,0x06,0x57,0xBE,0xD7,0x79,0xE8,0x66,0x07, +0x8B,0xD8,0xE8,0x61,0x07,0x8B,0xC8,0x2B,0xCB,0x78,0x11,0x8E,0xC3,0xBF,0x00,0x00, +0xB8,0xFF,0xFF,0x51,0xB9,0x08,0x00,0xF3,0xAB,0x59,0xE2,0xF7,0x5F,0x07,0xC3,0x90, +0x06,0xBE,0xD7,0x79,0xE8,0x3F,0x07,0x8B,0xD8,0xD1,0xE3,0x2E,0x8B,0x9F,0x44,0x00, +0xBE,0x08,0x52,0xE8,0xF1,0x08,0x8B,0xC3,0xE8,0xDD,0x08,0xB8,0x01,0x00,0xE8,0x73, +0xF2,0xE8,0xE0,0x08,0xBE,0x11,0x52,0xE8,0xDD,0x08,0x8B,0x47,0x18,0xE8,0xC8,0x08, +0xBE,0x59,0x52,0xE8,0xD1,0x08,0x8B,0x47,0x26,0xE8,0xBC,0x08,0xBE,0x35,0x52,0xE8, +0xC5,0x08,0x8B,0x47,0x1E,0xE8,0xB0,0x08,0xBE,0x3E,0x52,0xE8,0xB9,0x08,0x8B,0x47, +0x20,0xE8,0xA4,0x08,0xBE,0x50,0x52,0xE8,0xAD,0x08,0x8B,0x47,0x24,0xE8,0x98,0x08, +0xBE,0x62,0x52,0xE8,0xA1,0x08,0x8B,0x47,0x2A,0xE8,0x8C,0x08,0xE8,0x95,0x08,0xBE, +0x1A,0x52,0xE8,0x92,0x08,0x8B,0x07,0xE8,0x7E,0x08,0xBE,0x23,0x52,0xE8,0x87,0x08, +0x8B,0x47,0x1A,0xE8,0x72,0x08,0xBE,0x2C,0x52,0xE8,0x7B,0x08,0x8B,0x47,0x1C,0xE8, +0x66,0x08,0xBE,0x47,0x52,0xE8,0x6F,0x08,0x8B,0x47,0x22,0xE8,0x5A,0x08,0xE8,0x63, +0x08,0xBE,0xB3,0x52,0xE8,0x60,0x08,0x8B,0x47,0x38,0xE8,0x4B,0x08,0xBE,0x8F,0x52, +0xE8,0x54,0x08,0x8B,0x47,0x30,0xE8,0x3F,0x08,0xBE,0x98,0x52,0xE8,0x48,0x08,0x8B, +0x47,0x32,0xE8,0x33,0x08,0xBE,0x86,0x52,0xE8,0x3C,0x08,0x8B,0x47,0x2E,0xE8,0x27, +0x08,0xBE,0xA1,0x52,0xE8,0x30,0x08,0x8B,0x47,0x34,0xE8,0x1B,0x08,0xE8,0x24,0x08, +0xBE,0x6B,0x52,0xE8,0x21,0x08,0x8B,0x47,0x04,0xE8,0x0C,0x08,0xBE,0x74,0x52,0xE8, +0x15,0x08,0x8B,0x47,0x14,0xE8,0x00,0x08,0xBE,0x7D,0x52,0xE8,0x09,0x08,0x8B,0x47, +0x2C,0xE8,0xF4,0x07,0xBE,0xAA,0x52,0xE8,0xFD,0x07,0x8B,0x47,0x36,0xE8,0xE8,0x07, +0xBE,0xBC,0x52,0xE8,0xF1,0x07,0x8B,0x47,0x3A,0xE8,0xDC,0x07,0xBE,0xC5,0x52,0xE8, +0xE5,0x07,0x8B,0x47,0x3C,0xE8,0xD0,0x07,0xE8,0xD9,0x07,0xBE,0xFB,0x52,0xE8,0xD6, +0x07,0x8B,0x47,0x48,0xE8,0xC1,0x07,0xBE,0xE0,0x52,0xE8,0xCA,0x07,0x8B,0x47,0x42, +0xE8,0xB5,0x07,0xBE,0xE9,0x52,0xE8,0xBE,0x07,0x8B,0x47,0x44,0xE8,0xA9,0x07,0xBE, +0x5E,0x53,0xE8,0xB2,0x07,0x8B,0x47,0x4C,0xE8,0x9D,0x07,0xBE,0x67,0x53,0xE8,0xA6, +0x07,0x8B,0x47,0x4E,0xE8,0x91,0x07,0xBE,0x70,0x53,0xE8,0x9A,0x07,0x8B,0x47,0x50, +0xE8,0x85,0x07,0xE8,0x8E,0x07,0xBE,0x04,0x53,0xE8,0x8B,0x07,0x8B,0x47,0x4A,0xE8, +0x76,0x07,0xBE,0xCE,0x52,0xE8,0x7F,0x07,0x8B,0x47,0x08,0xE8,0x6A,0x07,0xBE,0xD7, +0x52,0xE8,0x73,0x07,0x8B,0x47,0x40,0xE8,0x5E,0x07,0xBE,0xF2,0x52,0xE8,0x67,0x07, +0x8B,0x47,0x46,0xE8,0x52,0x07,0xE8,0x5B,0x07,0xBE,0x4C,0x53,0xE8,0x58,0x07,0x8B, +0x47,0x7A,0xE8,0x43,0x07,0xBE,0x1F,0x53,0xE8,0x4C,0x07,0x8B,0x47,0x70,0xE8,0x37, +0x07,0xBE,0x28,0x53,0xE8,0x40,0x07,0x8B,0x47,0x72,0xE8,0x2B,0x07,0xBE,0x31,0x53, +0xE8,0x34,0x07,0x8B,0x47,0x74,0xE8,0x1F,0x07,0xE8,0x28,0x07,0xBE,0x0D,0x53,0xE8, +0x25,0x07,0x8B,0x47,0x0C,0xE8,0x10,0x07,0xBE,0x16,0x53,0xE8,0x19,0x07,0x8B,0x47, +0x10,0xE8,0x04,0x07,0xBE,0x3A,0x53,0xE8,0x0D,0x07,0x8B,0x47,0x76,0xE8,0xF8,0x06, +0xBE,0x43,0x53,0xE8,0x01,0x07,0x8B,0x47,0x78,0xE8,0xEC,0x06,0xBE,0x55,0x53,0xE8, +0xF5,0x06,0x8B,0x47,0x3E,0xE8,0xE0,0x06,0xE8,0xE9,0x06,0xBE,0x79,0x53,0xE8,0xE6, +0x06,0x8B,0x47,0x52,0xE8,0xD1,0x06,0xBE,0x82,0x53,0xE8,0xDA,0x06,0x8B,0x47,0x54, +0xE8,0xC5,0x06,0xBE,0x8B,0x53,0xE8,0xCE,0x06,0x8B,0x47,0x56,0xE8,0xB9,0x06,0xBE, +0x94,0x53,0xE8,0xC2,0x06,0x8B,0x47,0x58,0xE8,0xAD,0x06,0xBE,0x9D,0x53,0xE8,0xB6, +0x06,0x8B,0x47,0x5A,0xE8,0xA1,0x06,0xBE,0xA6,0x53,0xE8,0xAA,0x06,0x8B,0x47,0x5C, +0xE8,0x95,0x06,0xE8,0x9E,0x06,0xBE,0xAF,0x53,0xE8,0x9B,0x06,0x8B,0x47,0x5E,0xE8, +0x86,0x06,0xBE,0xB8,0x53,0xE8,0x8F,0x06,0x8B,0x47,0x60,0xE8,0x7A,0x06,0xBE,0xC1, +0x53,0xE8,0x83,0x06,0x8B,0x47,0x62,0xE8,0x6E,0x06,0xBE,0xCA,0x53,0xE8,0x77,0x06, +0x8B,0x47,0x7C,0xE8,0x62,0x06,0xBE,0xD3,0x53,0xE8,0x6B,0x06,0x8B,0x47,0x7E,0xE8, +0x56,0x06,0xBE,0xDC,0x53,0xE8,0x5F,0x06,0x8B,0x87,0x80,0x00,0xE8,0x49,0x06,0xE8, +0x52,0x06,0xBE,0x24,0x54,0xE8,0x4F,0x06,0x8B,0x87,0x9E,0x00,0xE8,0x39,0x06,0xBE, +0xE5,0x53,0xE8,0x42,0x06,0x8B,0x47,0x64,0xE8,0x2D,0x06,0xBE,0xEE,0x53,0xE8,0x36, +0x06,0x8B,0x47,0x6E,0xE8,0x21,0x06,0xBE,0xF7,0x53,0xE8,0x2A,0x06,0x8B,0x87,0x8E, +0x00,0xE8,0x14,0x06,0xBE,0x00,0x54,0xE8,0x1D,0x06,0x8B,0x87,0x90,0x00,0xE8,0x07, +0x06,0xBE,0x09,0x54,0xE8,0x10,0x06,0x8B,0x87,0x92,0x00,0xE8,0xFA,0x05,0xE8,0x03, +0x06,0xBE,0x12,0x54,0xE8,0x00,0x06,0x8B,0x87,0x94,0x00,0xE8,0xEA,0x05,0xBE,0x1B, +0x54,0xE8,0xF3,0x05,0x8B,0x87,0x96,0x00,0xE8,0xDD,0x05,0xBE,0x51,0x54,0xE8,0xE6, +0x05,0x8B,0x87,0x98,0x00,0xE8,0xD0,0x05,0xBE,0x3F,0x54,0xE8,0xD9,0x05,0x8A,0x87, +0xA0,0x00,0xE8,0xA7,0x05,0xBE,0x36,0x54,0xE8,0xCC,0x05,0x8A,0x47,0x28,0xE8,0x9B, +0x05,0xBE,0x48,0x54,0xE8,0xC0,0x05,0x8A,0x87,0xA1,0x00,0xE8,0x8E,0x05,0xE8,0xB3, +0x05,0xBE,0x5A,0x54,0xE8,0xB0,0x05,0x8A,0x87,0xA2,0x00,0xE8,0x7E,0x05,0xBE,0x63, +0x54,0xE8,0xA3,0x05,0x8A,0x87,0xA3,0x00,0xE8,0x71,0x05,0xBE,0x6C,0x54,0xE8,0x96, +0x05,0x8A,0x87,0xA4,0x00,0xE8,0x64,0x05,0xBE,0x75,0x54,0xE8,0x89,0x05,0x8A,0x87, +0xA5,0x00,0xE8,0x57,0x05,0xBE,0x7E,0x54,0xE8,0x7C,0x05,0x8A,0x87,0xA6,0x00,0xE8, +0x4A,0x05,0xBE,0x87,0x54,0xE8,0x6F,0x05,0x8A,0x87,0xA7,0x00,0xE8,0x3D,0x05,0xBE, +0x90,0x54,0xE8,0x62,0x05,0x8A,0x87,0xA8,0x00,0xE8,0x30,0x05,0xE8,0x55,0x05,0xBE, +0x99,0x54,0xE8,0x52,0x05,0x8A,0x87,0xA9,0x00,0xE8,0x20,0x05,0xBE,0xA2,0x54,0xE8, +0x45,0x05,0x8A,0x87,0xAA,0x00,0xE8,0x13,0x05,0xBE,0xAB,0x54,0xE8,0x38,0x05,0x8A, +0x87,0xAB,0x00,0xE8,0x06,0x05,0xBE,0xB4,0x54,0xE8,0x2B,0x05,0x8A,0x87,0xAD,0x00, +0xE8,0xF9,0x04,0xBE,0xBD,0x54,0xE8,0x1E,0x05,0x8A,0x87,0xAE,0x00,0xE8,0xEC,0x04, +0xBE,0xC6,0x54,0xE8,0x11,0x05,0x8A,0x87,0xAF,0x00,0xE8,0xDF,0x04,0xBE,0xCF,0x54, +0xE8,0x04,0x05,0x8A,0x87,0xB0,0x00,0xE8,0xD2,0x04,0xE8,0xF7,0x04,0xBE,0xD8,0x54, +0xE8,0xF4,0x04,0x8A,0x87,0xB1,0x00,0xE8,0xC2,0x04,0xBE,0xE1,0x54,0xE8,0xE7,0x04, +0x8A,0x87,0xB2,0x00,0xE8,0xB5,0x04,0xBE,0xEA,0x54,0xE8,0xDA,0x04,0x8A,0x87,0xB3, +0x00,0xE8,0xA8,0x04,0xBE,0xF3,0x54,0xE8,0xCD,0x04,0x8A,0x87,0xBB,0x00,0xE8,0x9B, +0x04,0xE8,0xC0,0x04,0xBE,0xFC,0x54,0xE8,0xBD,0x04,0x8A,0x87,0xBC,0x00,0xE8,0x8B, +0x04,0xBE,0x05,0x55,0xE8,0xB0,0x04,0x8A,0x87,0xBE,0x00,0xE8,0x7E,0x04,0xBE,0x0E, +0x55,0xE8,0xA3,0x04,0x8A,0x87,0xBF,0x00,0xE8,0x71,0x04,0xE8,0x96,0x04,0x07,0xC3, +0x60,0x06,0x1E,0x16,0x8B,0xEC,0xFF,0x4E,0x16,0xF7,0x46,0x1A,0x00,0x02,0x74,0x01, +0xFB,0xB8,0x00,0x00,0x8E,0xD8,0x8E,0xC0,0x89,0x2E,0x2D,0x7A,0xE8,0xCB,0x00,0x81, +0x66,0x1A,0xFF,0xFE,0xC6,0x06,0x2A,0x7A,0x00,0xE8,0xD8,0x00,0xB8,0xE2,0x5E,0xA3, +0x2B,0x7A,0xE8,0x5D,0x00,0x80,0x3E,0x2A,0x7A,0x00,0x74,0x0A,0x81,0x4E,0x1A,0x00, +0x01,0xC6,0x06,0x2A,0x7A,0x00,0x17,0x1F,0x07,0x61,0xCF,0x90,0x60,0x06,0x1E,0x16, +0x8B,0xEC,0xF7,0x46,0x1A,0x00,0x02,0x74,0x01,0xFB,0xB8,0x00,0x00,0x8E,0xD8,0x8E, +0xC0,0x89,0x2E,0x2D,0x7A,0x81,0x66,0x1A,0xFF,0xFE,0xC6,0x06,0x2A,0x7A,0x00,0xE8, +0x92,0x00,0xB8,0xE2,0x5E,0xA3,0x2B,0x7A,0xE8,0x17,0x00,0x80,0x3E,0x2A,0x7A,0x00, +0x74,0x0A,0x81,0x4E,0x1A,0x00,0x01,0xC6,0x06,0x2A,0x7A,0x00,0x17,0x1F,0x07,0x61, +0xCF,0x90,0xB8,0xF0,0x00,0xE8,0x8C,0xED,0xFF,0x26,0x2B,0x7A,0xC3,0x90,0x06,0x53, +0x56,0x80,0x3E,0x29,0x7A,0x00,0x74,0x03,0xE8,0x3F,0x00,0xBE,0xD7,0x79,0xE8,0x25, +0x02,0x8B,0xD8,0xA3,0x27,0x7A,0x2E,0x8A,0x07,0xA2,0x29,0x7A,0xB0,0xCC,0x2E,0x88, +0x07,0x5E,0x5B,0x07,0xC3,0xC6,0x06,0x2A,0x7A,0x00,0xB8,0xEC,0x5E,0xA3,0x2B,0x7A, +0xC3,0x90,0x8B,0x2E,0x2D,0x7A,0xE8,0x2B,0x00,0xC3,0xC6,0x06,0x2A,0x7A,0x01,0xE8, +0x08,0x00,0xB8,0xEC,0x5E,0xA3,0x2B,0x7A,0xC3,0x90,0x57,0x80,0x3E,0x29,0x7A,0x00, +0x74,0x0F,0x8B,0x3E,0x27,0x7A,0xA0,0x29,0x7A,0x2E,0x88,0x05,0xC6,0x06,0x29,0x7A, +0x00,0x5F,0xC3,0x90,0xBE,0x94,0x4D,0xE8,0x06,0x02,0xBE,0xBA,0x51,0xE8,0x00,0x02, +0xFF,0x76,0x14,0x58,0xE8,0x47,0x02,0xBE,0xC0,0x51,0xE8,0xF3,0x01,0xFF,0x76,0x0E, +0x58,0xE8,0x3A,0x02,0xBE,0xC6,0x51,0xE8,0xE6,0x01,0xFF,0x76,0x12,0x58,0xE8,0x2D, +0x02,0xBE,0xCC,0x51,0xE8,0xD9,0x01,0xFF,0x76,0x10,0x58,0xE8,0x20,0x02,0xBE,0xF6, +0x51,0xE8,0xCC,0x01,0xFF,0x76,0x0A,0x58,0xE8,0x13,0x02,0xBE,0xFC,0x51,0xE8,0xBF, +0x01,0xFF,0x76,0x0C,0x58,0xE8,0x06,0x02,0xBE,0xB1,0x51,0xE8,0xB2,0x01,0xFF,0x76, +0x1A,0x58,0xE8,0xF9,0x01,0xBE,0x94,0x4D,0xE8,0xA5,0x01,0xBE,0xD2,0x51,0xE8,0x9F, +0x01,0xFF,0x76,0x18,0x58,0xE8,0xE6,0x01,0xBE,0xD8,0x51,0xE8,0x92,0x01,0xFF,0x76, +0x02,0x58,0xE8,0xD9,0x01,0xBE,0xDE,0x51,0xE8,0x85,0x01,0xFF,0x76,0x04,0x58,0xE8, +0xCC,0x01,0xBE,0xE4,0x51,0xE8,0x78,0x01,0xFF,0x76,0x00,0x58,0xE8,0xBF,0x01,0xBE, +0xEA,0x51,0xE8,0x6B,0x01,0xFF,0x76,0x06,0x58,0xE8,0xB2,0x01,0xBE,0xF0,0x51,0xE8, +0x5E,0x01,0xFF,0x76,0x08,0x58,0xE8,0xA5,0x01,0xBE,0x02,0x52,0xE8,0x51,0x01,0xFF, +0x76,0x16,0x58,0xE8,0x98,0x01,0xBE,0x6B,0x4D,0xE8,0x44,0x01,0xC3,0x90,0xBE,0xAB, +0x4D,0xE8,0x3C,0x01,0xC3,0x3C,0x00,0x74,0x05,0x3C,0x01,0x74,0x59,0xC3,0xC7,0x06, +0x0C,0x7A,0xAF,0x50,0xC7,0x06,0x0E,0x7A,0xD2,0x50,0xC7,0x06,0x10,0x7A,0xCA,0x50, +0xC7,0x06,0x12,0x7A,0xCE,0x50,0xC7,0x06,0x14,0x7A,0xD6,0x50,0xC7,0x06,0x16,0x7A, +0xDD,0x50,0xC7,0x06,0x18,0x7A,0xE5,0x50,0xC7,0x06,0x1A,0x7A,0xED,0x50,0xC7,0x06, +0x1C,0x7A,0xF0,0x50,0xC7,0x06,0x1E,0x7A,0xF2,0x50,0xC7,0x06,0x20,0x7A,0xF4,0x50, +0xC7,0x06,0x22,0x7A,0xF8,0x50,0xC6,0x06,0x24,0x7A,0x01,0xC6,0x06,0x25,0x7A,0x01, +0xC6,0x06,0x26,0x7A,0x03,0xC3,0xC7,0x06,0x0C,0x7A,0xFC,0x50,0xC7,0x06,0x0E,0x7A, +0x2F,0x51,0xC7,0x06,0x10,0x7A,0x29,0x51,0xC7,0x06,0x12,0x7A,0x2C,0x51,0xC7,0x06, +0x14,0x7A,0x31,0x51,0xC7,0x06,0x16,0x7A,0x33,0x51,0xC7,0x06,0x18,0x7A,0x37,0x51, +0xC7,0x06,0x1A,0x7A,0x38,0x51,0xC7,0x06,0x1C,0x7A,0x3B,0x51,0xC7,0x06,0x1E,0x7A, +0x3C,0x51,0xC7,0x06,0x20,0x7A,0x3D,0x51,0xC7,0x06,0x22,0x7A,0x40,0x51,0xC6,0x06, +0x24,0x7A,0x20,0xC6,0x06,0x25,0x7A,0x20,0xC6,0x06,0x26,0x7A,0x02,0xC3,0xA1,0xF8, +0x79,0x48,0x74,0x14,0xBE,0xD7,0x79,0xE8,0x3C,0x00,0x8B,0xF8,0xAC,0x3C,0x3A,0x75, +0x07,0x8E,0xC7,0xE8,0x30,0x00,0x8B,0xF8,0xC3,0x90,0x8B,0xC7,0x2B,0x06,0xFE,0x79, +0x8A,0xF0,0x24,0x0F,0x8A,0xD0,0x02,0xD0,0x02,0xD0,0x80,0xC2,0x0B,0xC0,0xEE,0x04, +0x80,0xC6,0x03,0x04,0x3D,0xC3,0x8C,0xC0,0xE8,0x93,0x00,0xB0,0x3A,0xE8,0xE4,0x00, +0x8B,0xC7,0xE8,0x89,0x00,0xC3,0x51,0x33,0xC9,0x90,0xAC,0x3C,0x20,0x74,0xFB,0x90, +0x0A,0xC0,0x74,0x26,0x2C,0x30,0x72,0x22,0x3C,0x09,0x76,0x14,0x3C,0x11,0x72,0x1A, +0x2C,0x07,0x3C,0x0F,0x76,0x0A,0x3C,0x2A,0x72,0x10,0x2C,0x20,0x3C,0x0F,0x77,0x0A, +0x98,0xC1,0xE1,0x04,0x03,0xC8,0xAC,0xEB,0xD7,0x90,0x4E,0x8B,0xC1,0x59,0xC3,0x90, +0x06,0x8C,0xC8,0x8E,0xC0,0xE8,0x02,0x00,0x07,0xC3,0x26,0x8A,0x04,0x46,0x0A,0xC0, +0x74,0x06,0xE8,0x8F,0x00,0xEB,0xF3,0x90,0xC3,0x90,0x0B,0xC0,0x74,0x7A,0x51,0x33, +0xD2,0xB9,0xE8,0x03,0xF7,0xF1,0x8B,0xCA,0xE8,0x03,0x00,0x8B,0xC1,0x59,0xBA,0x64, +0x00,0xF6,0xF2,0xE8,0x0C,0x00,0x8A,0xC4,0x98,0xB2,0x0A,0xF6,0xF2,0xE8,0x02,0x00, +0x8A,0xC4,0x50,0x0A,0xF0,0x74,0x05,0x04,0x30,0xE8,0x58,0x00,0x58,0xC3,0x86,0xC4, +0xE8,0x07,0x00,0x86,0xC4,0xE8,0x02,0x00,0xC3,0x90,0xC1,0xC8,0x04,0xE8,0x08,0x00, +0xC1,0xC0,0x04,0xE8,0x02,0x00,0xC3,0x90,0x53,0x50,0x24,0x0F,0xBB,0xAC,0x62,0x2E, +0xD7,0xE8,0x30,0x00,0x58,0x5B,0xC3,0x90,0x86,0xC4,0xE8,0x07,0x00,0x86,0xC4,0xE8, +0x02,0x00,0xC3,0x90,0x50,0xB9,0x08,0x00,0x8A,0xE0,0x32,0xC0,0xD1,0xC0,0x04,0x30, +0xE8,0x11,0x00,0xE2,0xF5,0x58,0xC3,0x90,0xB0,0x30,0xE8,0x07,0x00,0xC3,0xB0,0x20, +0xE8,0x01,0x00,0xC3,0x56,0x8B,0x36,0xD0,0x79,0x88,0x84,0xD0,0x77,0x46,0x81,0xE6, +0xFF,0x01,0xFF,0x06,0xD4,0x79,0x89,0x36,0xD0,0x79,0x81,0x3E,0xD4,0x79,0xFE,0x01, +0x75,0x08,0x56,0xE8,0x14,0x00,0x5E,0xEB,0xF1,0x90,0x5E,0xC3,0xBA,0x02,0x02,0xEC, +0x24,0x01,0x74,0x04,0xBA,0x06,0x02,0xEC,0xC3,0x90,0x80,0x3E,0xF6,0x79,0x00,0x74, +0x09,0x60,0xB8,0x01,0x00,0xE8,0x2C,0xEA,0x61,0x90,0xBA,0x02,0x02,0xEC,0xA8,0x04, +0x74,0x28,0x8B,0x36,0xD2,0x79,0x83,0x3E,0xD4,0x79,0x00,0x74,0x1D,0x8A,0x84,0xD0, +0x77,0x46,0x81,0xE6,0xFF,0x01,0x89,0x36,0xD2,0x79,0xFF,0x0E,0xD4,0x79,0xBA,0x06, +0x02,0xEE,0xBA,0x02,0x02,0xEC,0xA8,0x04,0x75,0xDC,0xA1,0xD4,0x79,0xC3,0x52,0xBA, +0x06,0x02,0xEE,0x5A,0xC3,0x90,0x52,0x50,0xBA,0x02,0x02,0xEC,0xA8,0x04,0x74,0x08, +0x58,0x5A,0xE8,0xE9,0xFF,0xF9,0xC3,0x90,0x58,0x5A,0xF8,0xC3,0x52,0x50,0xBA,0x02, +0x02,0xEC,0xA8,0x04,0x74,0xFB,0x58,0x5A,0xE8,0xD3,0xFF,0xC3,0x30,0x31,0x32,0x33, +0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46,0x53,0x50,0x8A,0xE0, +0x80,0xE4,0x0F,0xBB,0xAC,0x62,0xC0,0xE8,0x04,0x2E,0xD7,0xE8,0xCE,0xFF,0x8A,0xC4, +0x2E,0xD7,0xE8,0xC7,0xFF,0x58,0x5B,0xC3,0x86,0xE0,0xE8,0xDF,0xFF,0x86,0xE0,0xE8, +0xDA,0xFF,0xC3,0x90,0xBE,0x94,0x4D,0x50,0x2E,0xAC,0x3C,0x00,0x74,0x05,0xE8,0xAB, +0xFF,0xEB,0xF5,0x58,0xC3,0x90,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0xBF, +0x04,0x00,0xC7,0x46,0xFC,0x00,0x00,0xC7,0x46,0xFA,0x00,0x00,0xC7,0x46,0xF8,0x00, +0x00,0x83,0x7E,0x06,0x00,0x75,0x0E,0x56,0xE8,0xB6,0x0E,0x59,0x0B,0xC0,0x75,0x05, +0x8B,0xC7,0xE9,0x5B,0x01,0x8B,0x46,0xFC,0x89,0x46,0xFE,0x0B,0xFF,0x75,0x05,0xB8, +0x01,0x00,0xEB,0x02,0x33,0xC0,0x50,0x56,0xE8,0xA4,0x0D,0x59,0x59,0xB4,0x00,0x89, +0x46,0xFC,0x8B,0x5E,0xFC,0x83,0xFB,0x08,0x76,0x03,0xE9,0x2B,0x01,0xD1,0xE3,0x2E, +0xFF,0xA7,0x94,0x64,0xB8,0x03,0x00,0xE9,0x26,0x01,0x83,0x7E,0xFA,0x00,0x74,0x14, +0xC7,0x46,0xFA,0x00,0x00,0x8A,0x44,0x58,0x98,0x50,0x8A,0x44,0x59,0x98,0x50,0xE8, +0xC2,0x0F,0x59,0x59,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00,0x00,0x56, +0xE8,0x9B,0x08,0x59,0x83,0x7E,0x06,0x00,0x75,0x05,0x8B,0xC7,0xE9,0xF1,0x00,0x83, +0xFF,0x04,0x75,0x03,0xE9,0xE6,0x00,0x8B,0xC7,0xE9,0xE4,0x00,0x83,0x7E,0xFE,0x00, +0x75,0x03,0xBF,0x02,0x00,0xE9,0xD5,0x00,0x83,0x7E,0xFE,0x00,0x75,0x03,0xBF,0x01, +0x00,0xE9,0xC9,0x00,0x8B,0x5E,0xFE,0x83,0xFB,0x07,0x76,0x03,0xE9,0x86,0x00,0xD1, +0xE3,0x2E,0xFF,0xA7,0x84,0x64,0x33,0xFF,0xE9,0x7F,0x00,0xBF,0x04,0x00,0x80,0x7C, +0x58,0x0F,0x74,0x22,0x83,0x7E,0xF8,0x00,0x75,0x1C,0xFE,0x44,0x58,0x6A,0x08,0x56, +0xE8,0x7E,0x0C,0x59,0x59,0x8A,0x44,0x58,0x04,0x80,0x50,0x56,0xE8,0x72,0x0C,0x59, +0x59,0xC7,0x46,0xFA,0x01,0x00,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00, +0x00,0x56,0xE8,0x19,0x08,0x59,0xEB,0x42,0xBF,0x04,0x00,0x80,0x7C,0x58,0x00,0x74, +0x22,0x83,0x7E,0xF8,0x00,0x75,0x1C,0xFE,0x4C,0x58,0x6A,0x08,0x56,0xE8,0x41,0x0C, +0x59,0x59,0x8A,0x44,0x58,0x04,0x80,0x50,0x56,0xE8,0x35,0x0C,0x59,0x59,0xC7,0x46, +0xFA,0x01,0x00,0x83,0x7E,0xF8,0x00,0x74,0x0A,0xC7,0x46,0xF8,0x00,0x00,0x56,0xE8, +0xDC,0x07,0x59,0xEB,0x05,0xBF,0x04,0x00,0xEB,0x00,0xEB,0x31,0xBF,0x04,0x00,0xEB, +0x2C,0xC7,0x46,0xF8,0x01,0x00,0x6A,0x08,0x56,0xE8,0x05,0x0C,0x59,0x59,0x80,0x7C, +0x58,0x09,0x7D,0x04,0xB0,0x0F,0xEB,0x02,0xB0,0x00,0x04,0x80,0x50,0x56,0xE8,0xF0, +0x0B,0x59,0x59,0xBF,0x04,0x00,0xEB,0x05,0xBF,0x04,0x00,0xEB,0x00,0xE9,0xA5,0xFE, +0x5F,0x5E,0xC9,0xC3,0xC6,0x63,0x45,0x64,0x45,0x64,0x45,0x64,0x45,0x64,0xCB,0x63, +0x08,0x64,0x33,0x64,0x5A,0x63,0x9C,0x63,0xA8,0x63,0x78,0x64,0xB4,0x63,0x4C,0x64, +0x4C,0x64,0x51,0x64,0x54,0x63,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x8B, +0x7E,0x08,0x6A,0x01,0x56,0xE8,0xA9,0x0B,0x59,0x59,0x8A,0x46,0x06,0xC0,0xE0,0x06, +0x04,0x80,0x50,0x56,0xE8,0x9A,0x0B,0x59,0x59,0xC7,0x46,0xFE,0x00,0x00,0x89,0x7E, +0xF8,0xEB,0x03,0xFF,0x46,0xFE,0x8B,0x5E,0xF8,0xFF,0x46,0xF8,0x80,0x3F,0x00,0x75, +0xF2,0x83,0x7E,0xFE,0x10,0x7D,0x25,0xB8,0x10,0x00,0x2B,0x46,0xFE,0xD1,0xF8,0x89, +0x46,0xFC,0xC7,0x46,0xFA,0x00,0x00,0xEB,0x0B,0x6A,0x20,0x56,0xE8,0x62,0x0B,0x59, +0x59,0xFF,0x46,0xFA,0x8B,0x46,0xFA,0x3B,0x46,0xFC,0x7C,0xED,0xEB,0x0C,0x8B,0xDF, +0x47,0x8A,0x07,0x50,0x56,0xE8,0x49,0x0B,0x59,0x59,0x80,0x3D,0x00,0x75,0xEF,0x6A, +0x02,0x56,0xE8,0x3C,0x0B,0x59,0x59,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x04,0x00, +0x00,0x56,0x57,0x8B,0x7E,0x04,0xC7,0x46,0xFE,0x00,0x00,0xBE,0x14,0x00,0xE9,0x09, +0x01,0x8B,0x5E,0xFE,0x83,0xC3,0x04,0x2B,0xDF,0x8A,0x87,0xAC,0x0B,0x88,0x44,0x5A, +0xC6,0x44,0x58,0x08,0x8A,0x46,0xFE,0x88,0x44,0x59,0xC7,0x44,0x06,0x00,0x00,0xC6, +0x44,0x19,0x00,0xC6,0x44,0x1A,0x00,0xC6,0x44,0x1B,0x00,0xC6,0x44,0x1D,0x0D,0xC6, +0x44,0x1E,0x03,0xC6,0x44,0x1F,0x00,0xC6,0x44,0x20,0x00,0xC6,0x44,0x21,0x00,0xC6, +0x44,0x5B,0x00,0xC6,0x44,0x5D,0x00,0xC6,0x44,0x5E,0x00,0xC6,0x44,0x5F,0x00,0xC6, +0x44,0x60,0x00,0xC7,0x46,0xFC,0x00,0x00,0xEB,0x0D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7, +0x40,0x30,0x00,0x00,0xFF,0x46,0xFC,0x83,0x7E,0xFC,0x10,0x7C,0xED,0xC7,0x46,0xFC, +0x00,0x00,0xEB,0x0A,0x8B,0x5E,0xFC,0xC6,0x40,0x50,0x00,0xFF,0x46,0xFC,0x83,0x7E, +0xFC,0x04,0x7C,0xF0,0xC7,0x44,0x54,0x00,0x00,0xC7,0x44,0x56,0x00,0x00,0x8A,0x44, +0x5A,0x98,0xBA,0xF8,0x00,0x23,0xD0,0xB8,0x05,0x00,0x0B,0xC2,0x89,0x46,0xFC,0x9C, +0xFA,0x8A,0x46,0xFC,0xBA,0xFE,0x00,0xEE,0xBA,0x00,0x00,0xEC,0x9D,0x24,0x08,0x88, +0x46,0xFC,0x83,0x7E,0xFC,0x00,0x75,0x02,0xEB,0x4A,0xFF,0x76,0xFE,0xE8,0x7A,0x0C, +0x59,0x68,0x35,0x02,0x56,0xE8,0x32,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x34,0x68,0x38, +0x02,0x56,0xE8,0x25,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x27,0x68,0x42,0x02,0x56,0xE8, +0x18,0x0A,0x59,0x59,0x0B,0xC0,0x75,0x1A,0x68,0x4C,0x02,0x56,0xE8,0x0B,0x0A,0x59, +0x59,0x0B,0xC0,0x75,0x0D,0x68,0x56,0x02,0x56,0xE8,0xFE,0x09,0x59,0x59,0x0B,0xC0, +0x74,0x02,0xEB,0x00,0xFF,0x46,0xFE,0x83,0xC6,0x62,0x39,0x7E,0xFE,0x7D,0x03,0xE9, +0xEF,0xFE,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x08,0x00,0x00,0x56,0x57,0x8B,0x46, +0x04,0xBA,0x62,0x00,0xF7,0xEA,0x05,0x14,0x00,0x8B,0xF0,0x83,0x7E,0x06,0x00,0x74, +0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x08,0x00,0x89,0x44,0x04,0x8A,0x46,0x08,0x88, +0x44,0x5C,0x56,0xE8,0x59,0x04,0x59,0x8B,0xF8,0x8B,0xC7,0x89,0x44,0x56,0x89,0x44, +0x54,0x8A,0x44,0x5D,0x88,0x44,0x2F,0x0B,0xFF,0x75,0x1D,0x68,0xC2,0x0F,0x6A,0x01, +0x56,0xE8,0x02,0xFE,0x83,0xC4,0x06,0xEB,0x00,0x6A,0x01,0x56,0xE8,0x47,0xFC,0x59, +0x59,0x0B,0xC0,0x75,0xF4,0xBF,0x01,0x00,0x89,0x7E,0xFA,0xB9,0x05,0x00,0xBB,0xCB, +0x6A,0x2E,0x8B,0x07,0x3B,0x46,0xFA,0x74,0x07,0x43,0x43,0xE2,0xF4,0xE9,0xA4,0x03, +0x2E,0xFF,0x67,0x0A,0xC7,0x44,0x06,0x02,0x00,0xC7,0x44,0x08,0xF4,0x08,0x8B,0x5E, +0x04,0xD1,0xE3,0x8B,0x87,0xFC,0x08,0x89,0x44,0x0A,0x33,0xC0,0x8B,0xF8,0x89,0x44, +0x54,0xE9,0x80,0x03,0x56,0xE8,0xBB,0x05,0x59,0xBF,0x01,0x00,0x8A,0x44,0x5D,0x88, +0x44,0x60,0xE9,0x6F,0x03,0x83,0x7C,0x04,0x08,0x75,0x30,0x80,0x7C,0x5C,0x01,0x75, +0x15,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0xE4,0x08,0x56,0xE8, +0xF7,0x08,0x59,0x59,0xEB,0x13,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF, +0xB7,0xC4,0x08,0x56,0xE8,0xE2,0x08,0x59,0x59,0xEB,0x2E,0x80,0x7C,0x5C,0x01,0x75, +0x15,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0xD4,0x08,0x56,0xE8, +0xC7,0x08,0x59,0x59,0xEB,0x13,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF, +0xB7,0xB4,0x08,0x56,0xE8,0xB2,0x08,0x59,0x59,0x6A,0x01,0x56,0xE8,0x87,0xFB,0x59, +0x59,0x8B,0xD8,0x83,0xFB,0x03,0x77,0x2A,0xD1,0xE3,0x2E,0xFF,0xA7,0xC3,0x6A,0xBF, +0x01,0x00,0x8A,0x44,0x5D,0x88,0x44,0x5E,0xEB,0x18,0x8A,0x44,0x5D,0x04,0xFF,0x24, +0x07,0x88,0x44,0x5D,0xEB,0x0C,0x8A,0x44,0x5D,0xFE,0xC0,0x24,0x07,0x88,0x44,0x5D, +0xEB,0x00,0xE9,0xCF,0x02,0x8A,0x44,0x5D,0xB4,0x00,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7, +0xFD,0x02,0x56,0xE8,0x63,0x08,0x59,0x59,0x68,0x1D,0x03,0x56,0xE8,0x5A,0x08,0x59, +0x59,0x6A,0x01,0x56,0xE8,0x2F,0xFB,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x03,0x77,0x36, +0xD1,0xE3,0x2E,0xFF,0xA7,0xBB,0x6A,0xBF,0x01,0x00,0x8A,0x44,0x5D,0x88,0x44,0x5F, +0xEB,0x24,0x8A,0x44,0x5D,0x04,0xFF,0x8A,0x54,0x04,0x80,0xC2,0xFF,0x22,0xC2,0x88, +0x44,0x5D,0xEB,0x12,0x8A,0x44,0x5D,0xFE,0xC0,0x8A,0x54,0x04,0x80,0xC2,0xFF,0x22, +0xC2,0x88,0x44,0x5D,0xEB,0x00,0xE9,0x6B,0x02,0x8B,0x5C,0x06,0x83,0xC3,0xFE,0xD1, +0xE3,0x8B,0x40,0x08,0x89,0x04,0x8B,0x1C,0xFF,0x77,0x06,0x6A,0x00,0x56,0xE8,0x85, +0xFC,0x83,0xC4,0x06,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x40,0x08,0x89,0x44,0x02, +0x8B,0x5C,0x02,0xFF,0x77,0x06,0x6A,0x01,0x56,0xE8,0x6A,0xFC,0x83,0xC4,0x06,0x6A, +0x01,0x56,0xE8,0xB1,0xFA,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x03,0x76,0x03,0xE9,0x1F, +0x02,0xD1,0xE3,0x2E,0xFF,0xA7,0xB3,0x6A,0x8B,0x5C,0x02,0x8B,0x47,0x04,0x89,0x44, +0x02,0x8B,0x5C,0x02,0x80,0x3F,0x44,0x75,0x0D,0x8B,0x5C,0x02,0x8A,0x47,0x01,0xB4, +0x00,0x3B,0x44,0x04,0x7D,0xE2,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x1C,0x03,0xD8,0x8B, +0x44,0x02,0x89,0x47,0x08,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x44,0x02,0x89,0x40, +0x08,0xE9,0xDE,0x01,0x8B,0x5C,0x02,0x8B,0x47,0x02,0x89,0x44,0x02,0x8B,0x5C,0x02, +0x80,0x3F,0x44,0x75,0x0D,0x8B,0x5C,0x02,0x8A,0x47,0x01,0xB4,0x00,0x3B,0x44,0x04, +0x7D,0xE2,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x1C,0x03,0xD8,0x8B,0x44,0x02,0x89,0x47, +0x08,0x8B,0x5C,0x06,0x4B,0xD1,0xE3,0x8B,0x44,0x02,0x89,0x40,0x08,0xE9,0xA2,0x01, +0xBF,0x01,0x00,0xE9,0x9C,0x01,0x8B,0x5C,0x02,0x8A,0x07,0xB4,0x00,0x89,0x46,0xF8, +0xB9,0x0C,0x00,0xBB,0x83,0x6A,0x2E,0x8B,0x07,0x3B,0x46,0xF8,0x74,0x07,0x43,0x43, +0xE2,0xF4,0xE9,0x77,0x01,0x2E,0xFF,0x67,0x18,0x8B,0x46,0x04,0xD1,0xE0,0x8B,0x5C, +0x02,0x03,0xD8,0x8B,0x47,0x08,0x8B,0x5C,0x06,0xFF,0x44,0x06,0xD1,0xE3,0x89,0x40, +0x08,0x8B,0x1C,0x80,0x7F,0x01,0x00,0x74,0x12,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B, +0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA,0x88,0x40,0x18,0xE9,0x40,0x01,0xFF,0x4C, +0x06,0xE9,0x3A,0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B,0x1C,0x8A,0x57,0x01,0xB6, +0x00,0x8B,0xDA,0x88,0x40,0x18,0xE9,0x25,0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B, +0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA,0x88,0x40,0x18,0xFF,0x4C,0x06,0xE9,0x0D, +0x01,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x8B,0x1C,0x8A,0x57,0x01,0xB6,0x00,0x8B,0xDA, +0x30,0x40,0x18,0xE9,0xF8,0x00,0xB8,0xF0,0x10,0x8B,0xF8,0x89,0x44,0x54,0x8A,0x44, +0x5F,0x88,0x44,0x5D,0xE9,0xE7,0x00,0x8A,0x44,0x1C,0x98,0x3D,0x02,0x00,0x74,0x07, +0x3D,0x03,0x00,0x74,0x02,0xEB,0x07,0xC7,0x46,0xFE,0x00,0x00,0xEB,0x2B,0x8A,0x44, +0x1C,0x98,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0x69,0x02,0x56,0xE8,0x6B,0x06,0x59,0x59, +0x6A,0x01,0x56,0xE8,0x40,0xF9,0x59,0x59,0x89,0x46,0xFE,0x83,0x7E,0xFE,0x00,0x74, +0x06,0x83,0x7E,0xFE,0x03,0x75,0xE9,0xEB,0x00,0x83,0x7E,0xFE,0x03,0x74,0x62,0x8A, +0x44,0x1C,0x98,0xD1,0xE0,0x8B,0xD8,0xFF,0xB7,0x6D,0x02,0x56,0xE8,0x3A,0x06,0x59, +0x59,0x56,0xE8,0x6B,0x97,0x59,0x89,0x46,0xFC,0x8B,0x5E,0xFC,0x83,0xEB,0xFE,0x83, +0xFB,0x03,0x77,0x33,0xD1,0xE3,0x2E,0xFF,0xA7,0x7B,0x6A,0x68,0xAC,0x02,0x56,0xE8, +0x17,0x06,0x59,0x59,0xEB,0x23,0x68,0x8F,0x02,0x56,0xE8,0x0C,0x06,0x59,0x59,0xEB, +0x18,0x68,0x75,0x02,0x56,0xE8,0x01,0x06,0x59,0x59,0xEB,0x0D,0x68,0xC6,0x02,0x56, +0xE8,0xF6,0x05,0x59,0x59,0xEB,0x02,0xEB,0x00,0x6A,0x01,0x56,0xE8,0xC7,0xF8,0x59, +0x59,0xBF,0x01,0x00,0xEB,0x38,0x68,0xDD,0x02,0x56,0xE8,0xDC,0x05,0x59,0x59,0x6A, +0x01,0x56,0xE8,0xB1,0xF8,0x59,0x59,0xBF,0x01,0x00,0xEB,0x22,0xB8,0xD0,0x30,0x8B, +0xF8,0x89,0x44,0x54,0x8A,0x44,0x60,0x88,0x44,0x5D,0xEB,0x12,0xB8,0xE0,0x20,0x8B, +0xF8,0x89,0x44,0x54,0x8A,0x44,0x5E,0x88,0x44,0x5D,0xEB,0x02,0xEB,0x00,0xEB,0x02, +0xEB,0x00,0xEB,0x00,0xE9,0x41,0xFC,0x5F,0x5E,0xC9,0xC3,0xFB,0x69,0x06,0x6A,0x11, +0x6A,0x1C,0x6A,0x00,0x00,0x01,0x00,0x02,0x00,0x04,0x00,0x41,0x00,0x42,0x00,0x43, +0x00,0x44,0x00,0x80,0x00,0x81,0x00,0x82,0x00,0xFF,0x00,0xF9,0x68,0x36,0x6A,0x5C, +0x6A,0x87,0x69,0x34,0x69,0x76,0x69,0x4C,0x6A,0x49,0x69,0x34,0x69,0x61,0x69,0x49, +0x69,0x2E,0x69,0xD6,0x68,0x58,0x68,0x94,0x68,0xD0,0x68,0xD7,0x67,0xE2,0x67,0xF4, +0x67,0xD7,0x67,0x7F,0x67,0x8A,0x67,0x96,0x67,0x7F,0x67,0x00,0x00,0x01,0x00,0xF0, +0x10,0xE0,0x20,0xD0,0x30,0x09,0x68,0xD4,0x66,0xA5,0x67,0x05,0x67,0xF4,0x66,0xC8, +0x04,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x8A,0x44,0x59,0x98,0x89,0x46,0xFC,0x6A, +0x09,0x8B,0x46,0xFC,0x05,0x84,0x01,0x50,0xE8,0x93,0x08,0x59,0x59,0x8B,0xF8,0x8B, +0xC7,0x25,0x00,0xF0,0x3D,0x00,0x10,0x75,0x55,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xF0, +0x00,0x75,0x4B,0x8B,0xC7,0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x8B,0x44, +0x04,0x3B,0x46,0xFE,0x7D,0x05,0x33,0xC0,0xE9,0xEF,0x00,0x8B,0xC7,0x25,0x0F,0x00, +0xBA,0x0F,0x00,0x2B,0xD0,0x3B,0x56,0xFE,0x74,0x05,0x33,0xC0,0xE9,0xDB,0x00,0xC7, +0x44,0x02,0x04,0x09,0x8A,0x46,0xFE,0x88,0x44,0x5F,0x88,0x44,0x5D,0x8B,0x5E,0xFC, +0xD1,0xE3,0xC7,0x87,0xFC,0x08,0x04,0x09,0xB8,0xF0,0x10,0xE9,0xBC,0x00,0x8B,0xC7, +0x25,0x00,0xF0,0x3D,0x00,0x20,0x75,0x52,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xE0,0x00, +0x75,0x48,0x8B,0xC7,0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x83,0x7E,0xFE, +0x08,0x7E,0x05,0x33,0xC0,0xE9,0x92,0x00,0x8B,0xC7,0x25,0x0F,0x00,0xBA,0x0F,0x00, +0x2B,0xD0,0x3B,0x56,0xFE,0x74,0x05,0x33,0xC0,0xEB,0x7F,0x90,0xC7,0x44,0x02,0x0C, +0x09,0x8A,0x46,0xFE,0x88,0x44,0x5E,0x88,0x44,0x5D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7, +0x87,0xFC,0x08,0x0C,0x09,0xB8,0xE0,0x20,0xEB,0x60,0x8B,0xC7,0x25,0x00,0xF0,0x3D, +0x00,0x30,0x75,0x52,0x8B,0xC7,0x25,0xF0,0x00,0x3D,0xD0,0x00,0x75,0x48,0x8B,0xC7, +0x25,0x00,0x0F,0xC1,0xF8,0x08,0x89,0x46,0xFE,0x8B,0x44,0x04,0x3B,0x46,0xFE,0x7D, +0x04,0x33,0xC0,0xEB,0x35,0x8B,0xC7,0x25,0x0F,0x00,0xBA,0x0F,0x00,0x2B,0xD0,0x3B, +0x56,0xFE,0x74,0x04,0x33,0xC0,0xEB,0x22,0xC7,0x44,0x02,0x14,0x09,0x8A,0x46,0xFE, +0x88,0x44,0x60,0x88,0x44,0x5D,0x8B,0x5E,0xFC,0xD1,0xE3,0xC7,0x87,0xFC,0x08,0x14, +0x09,0xB8,0xD0,0x30,0xEB,0x04,0x33,0xC0,0xEB,0x00,0x5F,0x5E,0xC9,0xC3,0xC8,0x06, +0x00,0x00,0x56,0x8B,0x76,0x04,0x6A,0x08,0x56,0xE8,0x35,0x04,0x59,0x59,0x8A,0x44, +0x58,0x04,0x80,0x50,0x56,0xE8,0x29,0x04,0x59,0x59,0x8B,0x44,0x54,0x3B,0x44,0x56, +0x75,0x0A,0x8A,0x44,0x5D,0x3A,0x44,0x2F,0x75,0x02,0xEB,0x64,0x8B,0x44,0x54,0x89, +0x44,0x56,0x8B,0x5C,0x02,0x8A,0x47,0x01,0x88,0x44,0x2F,0x8A,0x44,0x5D,0xB4,0x00, +0xC1,0xE0,0x08,0x8B,0x54,0x54,0x0B,0xD0,0x8A,0x44,0x5D,0xB4,0x00,0xBB,0x0F,0x00, +0x2B,0xD8,0x0B,0xD3,0x89,0x56,0xFE,0x6A,0x10,0x8A,0x44,0x59,0x98,0x05,0x04,0x00, +0x99,0x05,0x40,0x01,0x83,0xD2,0x00,0x52,0x50,0xE8,0x54,0x08,0x83,0xC4,0x06,0x89, +0x56,0xFC,0x89,0x46,0xFA,0x8B,0x46,0xFE,0x09,0x46,0xFA,0x83,0x4E,0xFC,0x00,0x6A, +0x19,0xFF,0x76,0xFC,0xFF,0x76,0xFA,0xE8,0x73,0x07,0x83,0xC4,0x06,0xE8,0xFE,0x07, +0x5E,0xC9,0xC3,0xC8,0x1C,0x00,0x00,0x56,0x57,0x8B,0x5E,0x04,0x8A,0x47,0x59,0x98, +0x8B,0xF0,0x8B,0x5E,0x04,0x8A,0x47,0x5D,0xB4,0x00,0x89,0x46,0xE6,0x83,0x7E,0xE6, +0x00,0x7D,0x0A,0x8B,0x5E,0x04,0x8B,0x47,0x04,0x48,0x89,0x46,0xE6,0x8B,0x5E,0x04, +0x8B,0x47,0x04,0x3B,0x46,0xE6,0x7F,0x05,0xC7,0x46,0xE6,0x00,0x00,0x8B,0x5E,0x04, +0x8A,0x46,0xE6,0x88,0x47,0x5D,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47, +0x02,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47,0x03,0x30,0x8B,0xDE, +0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x47,0x02,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F, +0x61,0x02,0xC6,0x47,0x03,0x30,0x8B,0x46,0xE6,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00, +0x74,0x18,0x8B,0x46,0xFA,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0x88,0x57,0x03,0xBB,0x0A,0x00,0x8B,0x46,0xFA, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74,0x18,0x8B,0x46,0xFA, +0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F, +0x59,0x02,0x88,0x57,0x02,0x8B,0x46,0xE6,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74, +0x18,0x8B,0x46,0xFA,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE, +0xD1,0xE3,0x8B,0x9F,0x61,0x02,0x88,0x57,0x03,0xBB,0x0A,0x00,0x8B,0x46,0xFA,0x33, +0xD2,0xF7,0xF3,0x89,0x46,0xFA,0x83,0x7E,0xFA,0x00,0x74,0x18,0x8B,0x46,0xFA,0xBB, +0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61, +0x02,0x88,0x57,0x02,0x8B,0x5E,0xE6,0xD1,0xE3,0xFF,0xB7,0x12,0x02,0x6A,0x00,0xFF, +0x76,0x04,0xE8,0xD1,0xF6,0x83,0xC4,0x06,0x68,0xD3,0x0F,0x6A,0x01,0xFF,0x76,0x04, +0xE8,0xC3,0xF6,0x83,0xC4,0x06,0xFF,0x76,0xE6,0x56,0xE8,0x1F,0x93,0x59,0x59,0x89, +0x56,0xF2,0x89,0x46,0xF0,0xFF,0x76,0xE6,0x56,0xE8,0x32,0x93,0x59,0x59,0x89,0x56, +0xEE,0x89,0x46,0xEC,0x9C,0xFA,0xC4,0x5E,0xF0,0x26,0x8B,0x07,0x89,0x46,0xEA,0xC4, +0x5E,0xEC,0x26,0x8B,0x07,0x89,0x46,0xE8,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFE,0x9D, +0xC7,0x46,0xE4,0x01,0x00,0xE8,0x08,0xA1,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFC,0x8B, +0x46,0xFC,0x2B,0x46,0xFE,0x3D,0xE8,0x03,0x73,0x03,0xE9,0x80,0x01,0x9C,0xFA,0xBA, +0x50,0xFF,0xED,0x89,0x46,0xFC,0x8B,0x46,0xFC,0x2B,0x46,0xFE,0x89,0x46,0xF8,0xC4, +0x5E,0xF0,0x26,0x8B,0x07,0x2B,0x46,0xEA,0x89,0x46,0xF6,0xC4,0x5E,0xF0,0x26,0x8B, +0x07,0x89,0x46,0xEA,0xC4,0x5E,0xEC,0x26,0x8B,0x07,0x2B,0x46,0xE8,0x89,0x46,0xF4, +0xC4,0x5E,0xEC,0x26,0x8B,0x07,0x89,0x46,0xE8,0xBA,0x50,0xFF,0xED,0x89,0x46,0xFE, +0x9D,0x81,0x7E,0xF8,0xE8,0x03,0x76,0x1C,0xFF,0x76,0xF8,0xFF,0x76,0xF6,0xE8,0x76, +0x01,0x59,0x59,0x89,0x46,0xF6,0xFF,0x76,0xF8,0xFF,0x76,0xF4,0xE8,0x68,0x01,0x59, +0x59,0x89,0x46,0xF4,0xBF,0x0E,0x00,0xEB,0x17,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59, +0x02,0xC6,0x01,0x20,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x01,0x20,0x47, +0x83,0xFF,0x11,0x76,0xE4,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0xC6,0x47,0x0D, +0x30,0x8B,0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0xC6,0x47,0x0D,0x30,0x83,0x7E,0xF6, +0x09,0x77,0x05,0xB8,0x0D,0x00,0xEB,0x26,0x83,0x7E,0xF6,0x63,0x77,0x05,0xB8,0x0E, +0x00,0xEB,0x1B,0x81,0x7E,0xF6,0xE7,0x03,0x77,0x05,0xB8,0x0F,0x00,0xEB,0x0F,0x81, +0x7E,0xF6,0x0F,0x27,0x77,0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x11,0x00,0x8B,0xF8, +0xEB,0x25,0x8B,0x46,0xF6,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x59,0x02,0x88,0x11,0x4F,0xBB,0x0A,0x00,0x8B,0x46,0xF6, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xF6,0x83,0x7E,0xF6,0x00,0x75,0xD5,0x83,0x7E,0xF4, +0x09,0x77,0x05,0xB8,0x0D,0x00,0xEB,0x26,0x83,0x7E,0xF4,0x63,0x77,0x05,0xB8,0x0E, +0x00,0xEB,0x1B,0x81,0x7E,0xF4,0xE7,0x03,0x77,0x05,0xB8,0x0F,0x00,0xEB,0x0F,0x81, +0x7E,0xF4,0x0F,0x27,0x77,0x05,0xB8,0x10,0x00,0xEB,0x03,0xB8,0x11,0x00,0x8B,0xF8, +0xEB,0x25,0x8B,0x46,0xF4,0xBB,0x0A,0x00,0x33,0xD2,0xF7,0xF3,0x80,0xC2,0x30,0x8B, +0xDE,0xD1,0xE3,0x8B,0x9F,0x61,0x02,0x88,0x11,0x4F,0xBB,0x0A,0x00,0x8B,0x46,0xF4, +0x33,0xD2,0xF7,0xF3,0x89,0x46,0xF4,0x83,0x7E,0xF4,0x00,0x75,0xD5,0x8B,0xDE,0xD1, +0xE3,0xFF,0xB7,0x59,0x02,0xFF,0x76,0x04,0xE8,0x6E,0x00,0x59,0x59,0x8B,0xDE,0xD1, +0xE3,0xFF,0xB7,0x61,0x02,0xFF,0x76,0x04,0xE8,0x5E,0x00,0x59,0x59,0x6A,0x00,0xFF, +0x76,0x04,0xE8,0x31,0xF3,0x59,0x59,0x8B,0xD8,0x83,0xFB,0x04,0x77,0x1F,0xD1,0xE3, +0x2E,0xFF,0xA7,0xFD,0x6F,0xEB,0x22,0xC7,0x46,0xE4,0x00,0x00,0xFF,0x4E,0xE6,0xEB, +0x0C,0xC7,0x46,0xE4,0x00,0x00,0xFF,0x46,0xE6,0xEB,0x02,0xEB,0x00,0x83,0x7E,0xE4, +0x00,0x74,0x03,0xE9,0x2A,0xFE,0xE9,0xD4,0xFC,0x5F,0x5E,0xC9,0xC3,0xD5,0x6F,0xD7, +0x6F,0xE1,0x6F,0xD5,0x6F,0xEB,0x6F,0x55,0x8B,0xEC,0x8B,0x46,0x04,0xB9,0xE8,0x03, +0xF7,0xE1,0x8B,0x4E,0x06,0xF7,0xF1,0x5D,0xC3,0x55,0x8B,0xEC,0x56,0x8B,0x76,0x06, +0xEB,0x0E,0x8B,0xDE,0x46,0x8A,0x07,0x50,0xFF,0x76,0x04,0xE8,0x33,0x00,0x59,0x59, +0x80,0x3C,0x00,0x75,0xED,0xEB,0x00,0x5E,0x5D,0xC3,0x55,0x8B,0xEC,0x56,0x8B,0x76, +0x06,0xEB,0x14,0x8B,0xDE,0x46,0x8A,0x07,0x50,0xFF,0x76,0x04,0xE8,0x45,0x00,0x59, +0x59,0x0B,0xC0,0x74,0x02,0xEB,0x07,0x80,0x3C,0x00,0x75,0xE7,0xEB,0x00,0x5E,0x5D, +0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE, +0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x02,0x74, +0x06,0x9D,0xE8,0xAB,0x9E,0xEB,0xE9,0xBA,0x00,0x00,0x8A,0x46,0x06,0xEE,0x9D,0xEB, +0x00,0x5E,0xC9,0xC3,0xC8,0x04,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98, +0x89,0x46,0xFE,0xE8,0x00,0xA2,0x89,0x46,0xFC,0xE8,0xFA,0xA1,0x2B,0x46,0xFC,0x3D, +0xB8,0x0B,0x76,0x05,0xB8,0x01,0x00,0xEB,0x23,0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE, +0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x02,0x74,0x06,0x9D,0xE8,0x62,0x9E,0xEB,0xD9, +0xBA,0x00,0x00,0x8A,0x46,0x06,0xEE,0x9D,0x33,0xC0,0xEB,0x00,0x5E,0xC9,0xC3,0xC8, +0x04,0x00,0x00,0x56,0x57,0x8B,0x76,0x04,0x83,0x7E,0x06,0x00,0x74,0x07,0x56,0xE8, +0x03,0x01,0x59,0xEB,0x05,0x56,0xE8,0xA2,0x00,0x59,0x88,0x46,0xFF,0x80,0x7E,0xFF, +0x08,0x77,0x06,0x8A,0x46,0xFF,0xE9,0x84,0x00,0x80,0x7E,0xFF,0x0F,0x76,0x03,0xEB, +0x79,0x90,0x8A,0x46,0xFF,0xB4,0x00,0x2D,0x0A,0x00,0x8B,0xD8,0x83,0xFB,0x04,0x77, +0x67,0xD1,0xE3,0x2E,0xFF,0xA7,0x91,0x71,0xB0,0x00,0xEB,0x61,0x56,0xE8,0x6B,0x00, +0x59,0xB4,0x00,0x25,0x0F,0x00,0x89,0x46,0xFC,0x56,0xE8,0x5E,0x00,0x59,0xB4,0x00, +0x8B,0xF8,0x56,0xE8,0x55,0x00,0x59,0xB4,0x00,0xC1,0xE0,0x08,0x8B,0xD7,0x03,0xD0, +0x8B,0xFA,0x8B,0x5E,0xFC,0xD1,0xE3,0x89,0x78,0x30,0xEB,0x2E,0x56,0xE8,0x3B,0x00, +0x59,0x88,0x44,0x5B,0xEB,0x24,0x56,0xE8,0x31,0x00,0x59,0x88,0x44,0x50,0x56,0xE8, +0x29,0x00,0x59,0x88,0x44,0x51,0x56,0xE8,0x21,0x00,0x59,0x88,0x44,0x52,0x56,0xE8, +0x19,0x00,0x59,0x88,0x44,0x53,0xEB,0x02,0xEB,0x00,0xE9,0x5B,0xFF,0x5F,0x5E,0xC9, +0xC3,0x28,0x71,0x88,0x71,0x2C,0x71,0x5C,0x71,0x66,0x71,0xC8,0x04,0x00,0x00,0x56, +0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE,0x9C,0xFA,0x8A,0x46,0xFE,0xBA, +0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x01,0x75,0x06,0x9D,0xE8,0x71,0x9D,0xEB, +0xE9,0xBA,0x00,0x00,0xEC,0x88,0x46,0xFD,0x9D,0x8A,0x46,0xFD,0xEB,0x00,0x5E,0xC9, +0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A,0x98,0x89,0x46,0xFE, +0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE,0x00,0xEE,0xBA,0x02,0x00,0xEC,0x32,0xE4,0x24, +0x01,0x9D,0x5E,0xC9,0xC3,0xC8,0x06,0x00,0x00,0x56,0x8B,0x76,0x04,0x8A,0x44,0x5A, +0x98,0x89,0x46,0xFE,0xE8,0x9F,0xA0,0x89,0x46,0xFA,0xE8,0x99,0xA0,0x2B,0x46,0xFA, +0x3D,0xB8,0x0B,0x76,0x04,0xB0,0x08,0xEB,0x24,0x9C,0xFA,0x8A,0x46,0xFE,0xBA,0xFE, +0x00,0xEE,0xBA,0x02,0x00,0xEC,0xA8,0x01,0x75,0x06,0x9D,0xE8,0x02,0x9D,0xEB,0xDA, +0xBA,0x00,0x00,0xEC,0x88,0x46,0xFD,0x9D,0x8A,0x46,0xFD,0xEB,0x00,0x5E,0xC9,0xC3, +0x55,0x8B,0xEC,0x56,0x8B,0x56,0x04,0x8A,0x46,0x06,0xEE,0x33,0xF6,0xEB,0x03,0x50, +0x58,0x46,0x83,0xFE,0x14,0x7C,0xF8,0x5E,0x5D,0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B, +0x56,0x04,0xEC,0x88,0x46,0xFF,0x33,0xF6,0xEB,0x03,0x50,0x58,0x46,0x83,0xFE,0x14, +0x7C,0xF8,0x8A,0x46,0xFF,0xEB,0x00,0x5E,0xC9,0xC3,0xC8,0x02,0x00,0x00,0x56,0x57, +0x8B,0x76,0x04,0x83,0x3E,0xB0,0x0B,0x00,0x75,0x1F,0xBA,0x88,0x01,0xB0,0x00,0xEE, +0xBA,0x86,0x01,0xB0,0x00,0xEE,0x6A,0x09,0x6A,0x00,0x68,0x30,0x01,0xE8,0x7D,0x01, +0x83,0xC4,0x06,0xC7,0x06,0xB0,0x0B,0x01,0x00,0x6A,0x09,0x8B,0xC6,0x05,0x80,0x01, +0x50,0xE8,0xDA,0x00,0x59,0x59,0x8B,0xF8,0x8B,0xC7,0xC1,0xE8,0x0C,0x25,0x0F,0x00, +0x89,0x46,0xFE,0x8B,0xC7,0xC1,0xE8,0x08,0x25,0x0F,0x00,0x8B,0x56,0xFE,0x83,0xF2, +0x0C,0x3B,0xC2,0x75,0x21,0x8B,0xC7,0xC1,0xE8,0x04,0x25,0x0F,0x00,0x8B,0x56,0xFE, +0x83,0xF2,0x06,0x3B,0xC2,0x75,0x0F,0x8B,0xC7,0x25,0x0F,0x00,0x8B,0x56,0xFE,0x83, +0xF2,0x09,0x3B,0xC2,0x74,0x0D,0x6A,0x07,0x56,0xE8,0x38,0x00,0x59,0x59,0xC7,0x46, +0xFE,0x07,0x00,0x8A,0x46,0xFE,0x04,0x80,0xA2,0x33,0x02,0x8B,0xC6,0xBA,0x62,0x00, +0xF7,0xEA,0x8A,0x56,0xFE,0x8B,0xD8,0x88,0x97,0x6C,0x00,0x68,0x32,0x02,0x8B,0xC6, +0xBA,0x62,0x00,0xF7,0xEA,0x05,0x14,0x00,0x50,0xE8,0x0E,0xFD,0x59,0x59,0xEB,0x00, +0x5F,0x5E,0xC9,0xC3,0xC8,0x02,0x00,0x00,0x56,0x8B,0x76,0x06,0x83,0xE6,0x0F,0x8B, +0xC6,0xC1,0xE0,0x0C,0x8B,0xD6,0x83,0xF2,0x0C,0xC1,0xE2,0x08,0x0B,0xC2,0x8B,0xD6, +0x83,0xF2,0x06,0xC1,0xE2,0x04,0x0B,0xC2,0x8B,0xD6,0x83,0xF2,0x09,0x0B,0xC2,0x89, +0x46,0xFE,0x6A,0x19,0x6A,0x10,0x8B,0x46,0x04,0x99,0x05,0x40,0x01,0x83,0xD2,0x00, +0x52,0x50,0xE8,0x6B,0x01,0x83,0xC4,0x06,0x0B,0x46,0xFE,0x83,0xCA,0x00,0x52,0x50, +0xE8,0x9A,0x00,0x83,0xC4,0x06,0xE8,0x25,0x01,0xEB,0x00,0x5E,0xC9,0xC3,0x55,0x8B, +0xEC,0x56,0x57,0x33,0xFF,0x6A,0x01,0x68,0x86,0x01,0xE8,0xA3,0xFE,0x59,0x59,0xB1, +0x10,0x2A,0x4E,0x06,0xD3,0x66,0x04,0x33,0xF6,0xEB,0x2E,0x81,0x7E,0x04,0x00,0x80, +0x72,0x04,0xB0,0x01,0xEB,0x02,0xB0,0x00,0x50,0x68,0x88,0x01,0xE8,0x81,0xFE,0x59, +0x59,0x6A,0x03,0x68,0x86,0x01,0xE8,0x77,0xFE,0x59,0x59,0x6A,0x01,0x68,0x86,0x01, +0xE8,0x6D,0xFE,0x59,0x59,0xD1,0x66,0x04,0x46,0x3B,0x76,0x06,0x7C,0xCD,0x33,0xF6, +0xEB,0x24,0xD1,0xE7,0x6A,0x03,0x68,0x86,0x01,0xE8,0x54,0xFE,0x59,0x59,0x6A,0x01, +0x68,0x86,0x01,0xE8,0x4A,0xFE,0x59,0x59,0x68,0x88,0x01,0xE8,0x5C,0xFE,0x59,0x98, +0x25,0x01,0x00,0x0B,0xF8,0x46,0x83,0xFE,0x10,0x7C,0xD7,0x6A,0x00,0x68,0x86,0x01, +0xE8,0x2D,0xFE,0x59,0x59,0x8B,0xC7,0xEB,0x00,0x5F,0x5E,0x5D,0xC3,0x55,0x8B,0xEC, +0x56,0x57,0x8B,0x7E,0x08,0x6A,0x01,0x68,0x86,0x01,0xE8,0x13,0xFE,0x59,0x59,0xB8, +0x20,0x00,0x2B,0xC7,0x50,0xFF,0x76,0x06,0xFF,0x76,0x04,0xE8,0xA2,0x00,0x83,0xC4, +0x06,0x89,0x56,0x06,0x89,0x46,0x04,0x33,0xF6,0xEB,0x47,0x81,0x7E,0x06,0x00,0x80, +0x72,0x0C,0x75,0x06,0x83,0x7E,0x04,0x00,0x72,0x04,0xB0,0x01,0xEB,0x02,0xB0,0x00, +0x50,0x68,0x88,0x01,0xE8,0xD9,0xFD,0x59,0x59,0x6A,0x03,0x68,0x86,0x01,0xE8,0xCF, +0xFD,0x59,0x59,0x6A,0x01,0x68,0x86,0x01,0xE8,0xC5,0xFD,0x59,0x59,0x6A,0x01,0xFF, +0x76,0x06,0xFF,0x76,0x04,0xE8,0x58,0x00,0x83,0xC4,0x06,0x89,0x56,0x06,0x89,0x46, +0x04,0x46,0x3B,0xF7,0x7C,0xB5,0x6A,0x00,0x68,0x86,0x01,0xE8,0xA2,0xFD,0x59,0x59, +0x6A,0x00,0x68,0x86,0x01,0xE8,0x98,0xFD,0x59,0x59,0x5F,0x5E,0x5D,0xC3,0x55,0x8B, +0xEC,0x56,0x6A,0x01,0x68,0x86,0x01,0xE8,0x86,0xFD,0x59,0x59,0x33,0xF6,0xEB,0x00, +0x68,0x88,0x01,0xE8,0x94,0xFD,0x59,0xA8,0x01,0x75,0x08,0x8B,0xC6,0x46,0x3D,0x64, +0x00,0x7C,0xED,0x6A,0x00,0x68,0x86,0x01,0xE8,0x65,0xFD,0x59,0x59,0x5E,0x5D,0xC3, +0xC8,0x04,0x00,0x00,0x8B,0x46,0x04,0x8B,0x56,0x06,0x8B,0x4E,0x08,0xE3,0x06,0xD1, +0xE0,0xD1,0xD2,0xE2,0xFA,0x89,0x46,0xFC,0x89,0x56,0xFE,0x8B,0x56,0xFE,0x8B,0x46, +0xFC,0xEB,0x00,0xC9,0xC3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x50,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x4D,0x65,0x6E,0x75,0x00,0x42,0x65, +0x67,0x69,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x6F,0x72,0x74, +0x20,0x30,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x00,0x50,0x6F,0x72,0x74,0x20,0x32, +0x00,0x50,0x6F,0x72,0x74,0x20,0x33,0x00,0x50,0x6F,0x72,0x74,0x20,0x34,0x00,0x50, +0x6F,0x72,0x74,0x20,0x35,0x00,0x50,0x6F,0x72,0x74,0x20,0x36,0x00,0x50,0x6F,0x72, +0x74,0x20,0x37,0x00,0x50,0x6F,0x72,0x74,0x20,0x38,0x00,0x50,0x6F,0x72,0x74,0x20, +0x39,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x30,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x31,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x32,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x33,0x00,0x50,0x6F,0x72,0x74,0x20,0x31,0x34,0x00,0x50,0x6F,0x72,0x74,0x20,0x31, +0x35,0x00,0x9C,0x01,0xA3,0x01,0xAA,0x01,0xB1,0x01,0xB8,0x01,0xBF,0x01,0xC6,0x01, +0xCD,0x01,0xD4,0x01,0xDB,0x01,0xE2,0x01,0xEA,0x01,0xF2,0x01,0xFA,0x01,0x02,0x02, +0x0A,0x02,0x08,0x00,0x00,0x07,0x81,0x00,0x03,0x80,0x80,0x80,0x9F,0x91,0x95,0x91, +0x9F,0x00,0x03,0x81,0x84,0x8E,0x95,0x84,0x84,0x84,0x84,0x00,0x03,0x82,0x84,0x84, +0x84,0x84,0x95,0x8E,0x84,0x00,0x04,0x88,0x00,0xB2,0x0B,0xC6,0x0B,0xDA,0x0B,0xEE, +0x0B,0x02,0x0C,0x16,0x0C,0x2A,0x0C,0x3E,0x0C,0x52,0x0C,0x77,0x0C,0x9C,0x0C,0xBE, +0x0C,0xE0,0x0C,0x02,0x0D,0x01,0x80,0x20,0x54,0x65,0x73,0x74,0x20,0x50,0x61,0x73, +0x73,0x65,0x64,0x20,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01, +0x80,0x20,0x4D,0x69,0x73,0x73,0x69,0x6E,0x67,0x20,0x52,0x78,0x20,0x44,0x61,0x74, +0x61,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20,0x42, +0x61,0x64,0x20,0x52,0x78,0x20,0x44,0x61,0x74,0x61,0x20,0x1F,0x20,0x50,0x72,0x65, +0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20,0x58,0x6D,0x74,0x72,0x20,0x42,0x75, +0x73,0x79,0x1F,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x02,0x00,0x01,0x80,0x20, +0x6E,0x6F,0x74,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x6C,0x79,0x1F,0x20,0x20, +0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x65,0x64,0x02,0x00,0x24,0x0D,0x2F, +0x0D,0x3A,0x0D,0x45,0x0D,0x50,0x0D,0x5B,0x0D,0x66,0x0D,0x71,0x0D,0x7C,0x0D,0x87, +0x0D,0x92,0x0D,0x9D,0x0D,0xA8,0x0D,0xB3,0x0D,0xBE,0x0D,0xC9,0x0D,0x53,0x80,0x2C, +0x32,0x54,0x44,0x20,0x53,0x86,0x2C,0x33,0x44,0x54,0x52,0x20,0x53,0x82,0x2C,0x33, +0x52,0x54,0x53,0x20,0x1F,0x53,0x81,0x2C,0x32,0x52,0x44,0x20,0x53,0x85,0x2C,0x32, +0x43,0x44,0x20,0x53,0x83,0x2C,0x33,0x43,0x54,0x53,0x20,0x53,0x84,0x2C,0x33,0x44, +0x53,0x52,0x20,0x53,0x87,0x2C,0x32,0x52,0x49,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x30,0x1F,0x27,0x53,0x85, +0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89, +0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x53,0x52, +0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x31,0x1F,0x27,0x53,0x84,0x2E,0x31,0x81, +0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C, +0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x43,0x54,0x53,0x20,0x2D,0x20, +0x70,0x69,0x6E,0x20,0x34,0x1F,0x27,0x53,0x83,0x2E,0x31,0x81,0x82,0x63,0x90,0x80, +0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27, +0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32, +0x32,0x1F,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84, +0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x2F,0x38,0x1F, +0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x27,0x53,0x82,0x2E, +0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F,0x27,0x53,0x81,0x2E,0x30,0x53,0x4D,0x81, +0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C, +0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78,0x44,0x20,0x2D,0x20, +0x70,0x69,0x6E,0x20,0x33,0x1F,0x27,0x53,0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63, +0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E, +0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69, +0x6E,0x20,0x35,0x1F,0x27,0x53,0x85,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F, +0x27,0x53,0x84,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x43,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x1F,0x27,0x53,0x83,0x2E, +0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D, +0x20,0x28,0x6E,0x2E,0x63,0x2E,0x29,0x1F,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63, +0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E, +0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69, +0x6E,0x20,0x32,0x1F,0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x37,0x1F, +0x27,0x53,0x82,0x2E,0x31,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x52,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x1F,0x27,0x53,0x81,0x2E, +0x30,0x53,0x4D,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88, +0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78, +0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x33,0x1F,0x27,0x53,0x80,0x2E,0x30,0x53, +0x4D,0x81,0x82,0x63,0x90,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A, +0x8B,0x8C,0x8D,0x8E,0x8F,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x43,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x85,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x84,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x43,0x54,0x53,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x31,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x83,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x28,0x6E,0x2E,0x63,0x2E,0x29,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x87,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x54,0x52,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x86,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x37,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x82,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x81,0x2E, +0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27, +0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20, +0x33,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63, +0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20, +0x44,0x43,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x30,0x1F,0x20,0x20,0x20, +0x20,0x27,0x53,0x85,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85, +0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x44,0x53,0x52,0x20,0x2D,0x20,0x70, +0x69,0x6E,0x20,0x31,0x31,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x84,0x2E,0x31,0x81, +0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x43,0x54,0x53,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x34,0x1F,0x20,0x20, +0x20,0x20,0x27,0x53,0x83,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84, +0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x49,0x20,0x2D,0x20,0x70, +0x69,0x6E,0x20,0x32,0x32,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x87,0x2E,0x31,0x81, +0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80, +0x20,0x20,0x44,0x54,0x52,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x36,0x2F,0x38,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x86,0x2E,0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82, +0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x52,0x54,0x53,0x20, +0x2D,0x20,0x70,0x69,0x6E,0x20,0x35,0x1F,0x20,0x20,0x20,0x20,0x27,0x53,0x82,0x2E, +0x31,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00, +0x01,0x80,0x20,0x20,0x52,0x78,0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x32,0x1F, +0x20,0x20,0x20,0x20,0x27,0x53,0x81,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80, +0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x27,0x02,0x00,0x01,0x80,0x20,0x20,0x54,0x78, +0x44,0x20,0x2D,0x20,0x70,0x69,0x6E,0x20,0x33,0x1F,0x20,0x20,0x20,0x20,0x27,0x53, +0x80,0x2E,0x30,0x53,0x4D,0x81,0x82,0x63,0x88,0x80,0x81,0x82,0x83,0x84,0x85,0x86, +0x87,0x27,0x02,0x00,0x68,0x04,0x96,0x04,0xB6,0x03,0x3C,0x04,0x0E,0x04,0x89,0x03, +0x5C,0x03,0xE2,0x03,0x60,0x08,0x8A,0x08,0xBE,0x07,0x38,0x08,0x0E,0x08,0x95,0x07, +0x6C,0x07,0xE6,0x07,0x1C,0x05,0x74,0x05,0xFA,0x05,0xC4,0x04,0xF0,0x04,0xCC,0x05, +0xA0,0x05,0x48,0x05,0x78,0x06,0xC8,0x06,0x42,0x07,0x28,0x06,0x50,0x06,0x18,0x07, +0xF0,0x06,0xA0,0x06,0x00,0x00,0xF4,0x08,0xF4,0x08,0xD4,0x0D,0x04,0x09,0x04,0x09, +0x04,0x09,0x04,0x09,0x42,0x00,0x0C,0x09,0x1C,0x09,0xE5,0x0D,0x02,0x00,0x14,0x09, +0x04,0x09,0xF4,0x0D,0x43,0x00,0x1C,0x09,0x0C,0x09,0x05,0x0E,0x00,0x04,0x04,0x09, +0x14,0x09,0x12,0x0E,0x2C,0x09,0x2C,0x09,0x2C,0x09,0x2C,0x09,0x00,0x00,0x3C,0x09, +0x6C,0x09,0x1E,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x01,0x4C,0x09, +0x2C,0x09,0x2D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x02,0x5C,0x09, +0x3C,0x09,0x3D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0x00,0x03,0x6C,0x09, +0x4C,0x09,0x4D,0x0E,0x74,0x09,0x74,0x09,0x74,0x09,0x74,0x09,0xFF,0x00,0x2C,0x09, +0x5C,0x09,0x00,0x00,0x00,0x05,0x84,0x09,0xEC,0x09,0x5E,0x0E,0xF4,0x09,0xF4,0x09, +0xF4,0x09,0xF4,0x09,0x00,0x06,0x94,0x09,0x74,0x09,0x68,0x0E,0xAC,0x0A,0xAC,0x0A, +0xAC,0x0A,0xAC,0x0A,0x00,0x07,0xA4,0x09,0x84,0x09,0x72,0x0E,0xBC,0x0A,0xBC,0x0A, +0xBC,0x0A,0xBC,0x0A,0x00,0x08,0xB4,0x09,0x94,0x09,0x7C,0x0E,0xD4,0x0A,0xD4,0x0A, +0xD4,0x0A,0xD4,0x0A,0x00,0x0B,0xC4,0x09,0xA4,0x09,0x83,0x0E,0xFC,0x0A,0xFC,0x0A, +0xFC,0x0A,0xFC,0x0A,0x00,0x0C,0xD4,0x09,0xB4,0x09,0x90,0x0E,0x14,0x0B,0x14,0x0B, +0x14,0x0B,0x14,0x0B,0x00,0x02,0xE4,0x09,0xC4,0x09,0xA0,0x0E,0x2C,0x0B,0x2C,0x0B, +0x2C,0x0B,0x2C,0x0B,0x04,0x00,0xEC,0x09,0xD4,0x09,0x0E,0x00,0xFF,0x00,0x74,0x09, +0xE4,0x09,0x00,0x00,0x82,0x01,0xFC,0x09,0xA4,0x0A,0xAC,0x0E,0x82,0x02,0x04,0x0A, +0xF4,0x09,0xAF,0x0E,0x82,0x03,0x0C,0x0A,0xFC,0x09,0xB2,0x0E,0x82,0x04,0x14,0x0A, +0x04,0x0A,0xB6,0x0E,0x82,0x05,0x1C,0x0A,0x0C,0x0A,0xBC,0x0E,0x82,0x06,0x24,0x0A, +0x14,0x0A,0xC0,0x0E,0x82,0x07,0x2C,0x0A,0x1C,0x0A,0xC4,0x0E,0x82,0x08,0x34,0x0A, +0x24,0x0A,0xC8,0x0E,0x82,0x09,0x3C,0x0A,0x2C,0x0A,0xCC,0x0E,0x82,0x0A,0x44,0x0A, +0x34,0x0A,0xD1,0x0E,0x82,0x10,0x4C,0x0A,0x3C,0x0A,0xD6,0x0E,0x82,0x0B,0x54,0x0A, +0x44,0x0A,0xDB,0x0E,0x82,0x11,0x5C,0x0A,0x4C,0x0A,0xE0,0x0E,0x82,0x0C,0x64,0x0A, +0x54,0x0A,0xE5,0x0E,0x82,0x12,0x6C,0x0A,0x5C,0x0A,0xEA,0x0E,0x82,0x0D,0x74,0x0A, +0x64,0x0A,0xEF,0x0E,0x82,0x0E,0x7C,0x0A,0x6C,0x0A,0xF4,0x0E,0x82,0x0F,0x84,0x0A, +0x74,0x0A,0xFB,0x0E,0x82,0x13,0x8C,0x0A,0x7C,0x0A,0x02,0x0F,0x82,0x14,0x94,0x0A, +0x84,0x0A,0x09,0x0F,0x82,0x15,0x9C,0x0A,0x8C,0x0A,0x10,0x0F,0x82,0x16,0xA4,0x0A, +0x94,0x0A,0x17,0x0F,0x82,0x17,0xF4,0x09,0x9C,0x0A,0x1E,0x0F,0x82,0x02,0xB4,0x0A, +0xB4,0x0A,0x26,0x0F,0x82,0x03,0xAC,0x0A,0xAC,0x0A,0x2D,0x0F,0x82,0x00,0xC4,0x0A, +0xCC,0x0A,0x34,0x0F,0x82,0x01,0xCC,0x0A,0xBC,0x0A,0x3F,0x0F,0x82,0x02,0xBC,0x0A, +0xC4,0x0A,0x4D,0x0F,0x82,0x00,0xDC,0x0A,0xF4,0x0A,0x59,0x0F,0x82,0x01,0xE4,0x0A, +0xD4,0x0A,0x63,0x0F,0x82,0x02,0xEC,0x0A,0xDC,0x0A,0x6E,0x0F,0x82,0x03,0xF4,0x0A, +0xE4,0x0A,0x7A,0x0F,0x82,0x04,0xD4,0x0A,0xEC,0x0A,0x87,0x0F,0x82,0x00,0x04,0x0B, +0x0C,0x0B,0x93,0x0F,0x82,0x01,0x0C,0x0B,0xFC,0x0A,0x9B,0x0F,0x82,0x02,0xFC,0x0A, +0x04,0x0B,0xA7,0x0F,0x82,0x00,0x1C,0x0B,0x24,0x0B,0xB0,0x0F,0x82,0x01,0x24,0x0B, +0x14,0x0B,0xB5,0x0F,0x82,0x02,0x14,0x0B,0x1C,0x0B,0xBE,0x0F,0x44,0x00,0x34,0x0B, +0xA4,0x0B,0x9C,0x01,0x44,0x01,0x3C,0x0B,0x2C,0x0B,0xA3,0x01,0x44,0x02,0x44,0x0B, +0x34,0x0B,0xAA,0x01,0x44,0x03,0x4C,0x0B,0x3C,0x0B,0xB1,0x01,0x44,0x04,0x54,0x0B, +0x44,0x0B,0xB8,0x01,0x44,0x05,0x5C,0x0B,0x4C,0x0B,0xBF,0x01,0x44,0x06,0x64,0x0B, +0x54,0x0B,0xC6,0x01,0x44,0x07,0x6C,0x0B,0x5C,0x0B,0xCD,0x01,0x44,0x08,0x74,0x0B, +0x64,0x0B,0xD4,0x01,0x44,0x09,0x7C,0x0B,0x6C,0x0B,0xDB,0x01,0x44,0x0A,0x84,0x0B, +0x74,0x0B,0xE2,0x01,0x44,0x0B,0x8C,0x0B,0x7C,0x0B,0xEA,0x01,0x44,0x0C,0x94,0x0B, +0x84,0x0B,0xF2,0x01,0x44,0x0D,0x9C,0x0B,0x8C,0x0B,0xFA,0x01,0x44,0x0E,0xA4,0x0B, +0x94,0x0B,0x02,0x02,0x44,0x0F,0x2C,0x0B,0x9C,0x0B,0x0A,0x02,0x17,0x1F,0x0F,0x2F, +0x00,0x00,0x01,0x80,0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A, +0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80,0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63, +0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80,0x78,0x78,0x3A,0x20, +0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0x80, +0x78,0x78,0x3A,0x20,0x74,0x78,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A, +0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A, +0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63, +0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0,0x78,0x78,0x3A,0x20, +0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A,0x02,0x00,0x01,0xC0, +0x78,0x78,0x3A,0x20,0x72,0x63,0x20,0x63,0x70,0x73,0x20,0x2A,0x2A,0x2A,0x2A,0x2A, +0x02,0x00,0x01,0x80,0x49,0x6E,0x73,0x74,0x61,0x6C,0x6C,0x20,0x4C,0x6F,0x6F,0x70, +0x62,0x61,0x63,0x6B,0x1F,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x20,0x74,0x6F,0x20, +0x73,0x74,0x61,0x72,0x74,0x02,0x00,0x01,0x80,0x20,0x43,0x61,0x62,0x6C,0x65,0x20, +0x74,0x6F,0x20,0x52,0x65,0x6D,0x6F,0x74,0x65,0x1F,0x50,0x72,0x65,0x73,0x73,0x20, +0x80,0x20,0x74,0x6F,0x20,0x73,0x74,0x61,0x72,0x74,0x02,0x00,0x01,0x80,0x20,0x4C, +0x6F,0x63,0x61,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x20,0x1F,0x20, +0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E,0x02,0x00,0x01,0x80, +0x52,0x65,0x6D,0x6F,0x74,0x65,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x20, +0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E,0x02,0x00, +0x01,0x80,0x20,0x49,0x6E,0x74,0x72,0x6E,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61, +0x63,0x6B,0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E,0x2E,0x2E, +0x02,0x00,0x01,0x80,0x54,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20,0x50,0x61,0x74, +0x74,0x65,0x72,0x6E,0x1F,0x20,0x20,0x52,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x2E, +0x2E,0x2E,0x02,0x00,0x01,0x80,0x20,0x20,0x30,0x3A,0x20,0x27,0x43,0x80,0x00,0x01, +0x80,0x20,0x20,0x31,0x3A,0x20,0x27,0x43,0x81,0x00,0x01,0x80,0x20,0x20,0x32,0x3A, +0x20,0x27,0x43,0x82,0x00,0x01,0x80,0x20,0x20,0x33,0x3A,0x20,0x27,0x43,0x83,0x00, +0x01,0x80,0x20,0x20,0x34,0x3A,0x20,0x27,0x43,0x84,0x00,0x01,0x80,0x20,0x20,0x35, +0x3A,0x20,0x27,0x43,0x85,0x00,0x01,0x80,0x20,0x20,0x36,0x3A,0x20,0x27,0x43,0x86, +0x00,0x01,0x80,0x20,0x20,0x37,0x3A,0x20,0x27,0x43,0x87,0x00,0x01,0x80,0x20,0x20, +0x38,0x3A,0x20,0x27,0x43,0x88,0x00,0x01,0x80,0x20,0x20,0x39,0x3A,0x20,0x27,0x43, +0x89,0x00,0x01,0x80,0x20,0x31,0x30,0x3A,0x20,0x27,0x43,0x8A,0x00,0x01,0x80,0x20, +0x31,0x31,0x3A,0x20,0x27,0x43,0x8B,0x00,0x01,0x80,0x20,0x31,0x32,0x3A,0x20,0x27, +0x43,0x8C,0x00,0x01,0x80,0x20,0x31,0x33,0x3A,0x20,0x27,0x43,0x8D,0x00,0x01,0x80, +0x20,0x31,0x34,0x3A,0x20,0x27,0x43,0x8E,0x00,0x01,0x80,0x20,0x31,0x35,0x3A,0x20, +0x27,0x43,0x8F,0x00,0x2A,0x2A,0x20,0x4D,0x61,0x69,0x6E,0x20,0x20,0x4D,0x65,0x6E, +0x75,0x20,0x2A,0x2A,0x00,0x4D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x20,0x50, +0x6F,0x72,0x74,0x00,0x4D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x20,0x53,0x69, +0x67,0x6E,0x61,0x6C,0x00,0x45,0x73,0x74,0x69,0x6D,0x61,0x74,0x65,0x20,0x43,0x50, +0x53,0x00,0x44,0x69,0x61,0x67,0x6E,0x6F,0x73,0x74,0x69,0x63,0x73,0x00,0x4C,0x6F, +0x63,0x61,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x52,0x65,0x6D, +0x6F,0x74,0x65,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x49,0x6E,0x74, +0x72,0x6E,0x6C,0x20,0x4C,0x6F,0x6F,0x70,0x62,0x61,0x63,0x6B,0x00,0x54,0x72,0x61, +0x6E,0x73,0x6D,0x69,0x74,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x00,0x42,0x61, +0x75,0x64,0x20,0x52,0x61,0x74,0x65,0x00,0x44,0x61,0x74,0x61,0x20,0x42,0x69,0x74, +0x73,0x00,0x53,0x74,0x6F,0x70,0x20,0x42,0x69,0x74,0x73,0x00,0x50,0x61,0x72,0x69, +0x74,0x79,0x00,0x44,0x61,0x74,0x61,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x00, +0x54,0x78,0x20,0x46,0x6C,0x6F,0x77,0x20,0x43,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x00, +0x50,0x6F,0x72,0x74,0x20,0x4E,0x75,0x6D,0x62,0x65,0x72,0x00,0x35,0x30,0x00,0x37, +0x35,0x00,0x31,0x31,0x30,0x00,0x31,0x33,0x34,0x2E,0x35,0x00,0x31,0x35,0x30,0x00, +0x32,0x30,0x30,0x00,0x33,0x30,0x30,0x00,0x36,0x30,0x30,0x00,0x31,0x32,0x30,0x30, +0x00,0x31,0x38,0x30,0x30,0x00,0x32,0x30,0x30,0x30,0x00,0x32,0x34,0x30,0x30,0x00, +0x33,0x36,0x30,0x30,0x00,0x34,0x38,0x30,0x30,0x00,0x37,0x32,0x30,0x30,0x00,0x39, +0x36,0x30,0x30,0x00,0x31,0x39,0x2C,0x32,0x30,0x30,0x00,0x33,0x38,0x2C,0x34,0x30, +0x30,0x00,0x35,0x36,0x2C,0x30,0x30,0x30,0x00,0x35,0x37,0x2C,0x36,0x30,0x30,0x00, +0x36,0x34,0x2C,0x30,0x30,0x30,0x00,0x37,0x36,0x2C,0x38,0x30,0x30,0x00,0x31,0x31, +0x35,0x2C,0x32,0x30,0x30,0x00,0x37,0x20,0x62,0x69,0x74,0x73,0x00,0x38,0x20,0x62, +0x69,0x74,0x73,0x00,0x31,0x20,0x73,0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x00,0x31, +0x2E,0x35,0x20,0x73,0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x73,0x00,0x32,0x20,0x73, +0x74,0x6F,0x70,0x20,0x62,0x69,0x74,0x73,0x00,0x6E,0x6F,0x20,0x70,0x61,0x72,0x69, +0x74,0x79,0x00,0x6F,0x64,0x64,0x20,0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x65,0x76, +0x65,0x6E,0x20,0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x73,0x70,0x61,0x63,0x65,0x20, +0x70,0x61,0x72,0x69,0x74,0x79,0x00,0x6D,0x61,0x72,0x6B,0x20,0x70,0x61,0x72,0x69, +0x74,0x79,0x00,0x43,0x6F,0x6C,0x75,0x6D,0x6E,0x73,0x00,0x42,0x61,0x72,0x62,0x65, +0x72,0x20,0x50,0x6F,0x6C,0x65,0x00,0x55,0x55,0x55,0x55,0x55,0x2E,0x2E,0x2E,0x00, +0x4E,0x6F,0x6E,0x65,0x00,0x58,0x6F,0x6E,0x2F,0x58,0x6F,0x66,0x66,0x00,0x43,0x54, +0x53,0x00,0x50,0x72,0x65,0x73,0x73,0x20,0x80,0x20,0x66,0x6F,0x72,0x20,0x6D,0x65, +0x6E,0x75,0x00,0x28,0x63,0x6F,0x75,0x6E,0x74,0x69,0x6E,0x67,0x2E,0x2E,0x2E,0x29, +0x00,0x00,0x65,0x4E,0x64,0x20,0x4F,0x66,0x20,0x43,0x6F,0x44,0x65,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2cmd.c linux/drivers/char/ip2/i2cmd.c --- v2.2.11/linux/drivers/char/ip2/i2cmd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2cmd.c Wed Aug 25 17:29:47 1999 @@ -0,0 +1,264 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definition table for In-line and Bypass commands. Applicable +* only when the standard loadware is active. (This is included +* source code, not a separate compilation module.) +* +*******************************************************************************/ + +//------------------------------------------------------------------------------ +// +// Revision History: +// +// 10 October 1991 MAG First Draft +// 7 November 1991 MAG Reflects additional commands. +// 24 February 1992 MAG Additional commands for 1.4.x loadware +// 11 March 1992 MAG Additional commands +// 30 March 1992 MAG Additional command: CMD_DSS_NOW +// 18 May 1992 MAG Discovered commands 39 & 40 must be at the end of a +// packet: affects implementation. +//------------------------------------------------------------------------------ + +//************ +//* Includes * +//************ + +#include "i2cmd.h" /* To get some bit-defines */ + +//------------------------------------------------------------------------------ +// Here is the table of global arrays which represent each type of command +// supported in the IntelliPort standard loadware. See also i2cmd.h +// for a more complete explanation of what is going on. +//------------------------------------------------------------------------------ + +// Here are the various globals: note that the names are not used except through +// the macros defined in i2cmd.h. Also note that although they are character +// arrays here (for extendability) they are cast to structure pointers in the +// i2cmd.h macros. See i2cmd.h for flags definitions. + +// Length Flags Command +static UCHAR ct02[] = { 1, BTH, 0x02 }; // DTR UP +static UCHAR ct03[] = { 1, BTH, 0x03 }; // DTR DN +static UCHAR ct04[] = { 1, BTH, 0x04 }; // RTS UP +static UCHAR ct05[] = { 1, BTH, 0x05 }; // RTS DN +static UCHAR ct06[] = { 1, BYP, 0x06 }; // START FL +static UCHAR ct07[] = { 2, BTH, 0x07,0 }; // BAUD +static UCHAR ct08[] = { 2, BTH, 0x08,0 }; // BITS +static UCHAR ct09[] = { 2, BTH, 0x09,0 }; // STOP +static UCHAR ct10[] = { 2, BTH, 0x0A,0 }; // PARITY +static UCHAR ct11[] = { 2, BTH, 0x0B,0 }; // XON +static UCHAR ct12[] = { 2, BTH, 0x0C,0 }; // XOFF +static UCHAR ct13[] = { 1, BTH, 0x0D }; // STOP FL +static UCHAR ct14[] = { 1, BYP|VIP, 0x0E }; // ACK HOTK +//static UCHAR ct15[]={ 2, BTH|VIP, 0x0F,0 }; // IRQ SET +static UCHAR ct16[] = { 2, INL, 0x10,0 }; // IXONOPTS +static UCHAR ct17[] = { 2, INL, 0x11,0 }; // OXONOPTS +static UCHAR ct18[] = { 1, INL, 0x12 }; // CTSENAB +static UCHAR ct19[] = { 1, BTH, 0x13 }; // CTSDSAB +static UCHAR ct20[] = { 1, INL, 0x14 }; // DCDENAB +static UCHAR ct21[] = { 1, BTH, 0x15 }; // DCDDSAB +static UCHAR ct22[] = { 1, BTH, 0x16 }; // DSRENAB +static UCHAR ct23[] = { 1, BTH, 0x17 }; // DSRDSAB +static UCHAR ct24[] = { 1, BTH, 0x18 }; // RIENAB +static UCHAR ct25[] = { 1, BTH, 0x19 }; // RIDSAB +static UCHAR ct26[] = { 2, BTH, 0x1A,0 }; // BRKENAB +static UCHAR ct27[] = { 1, BTH, 0x1B }; // BRKDSAB +//static UCHAR ct28[]={ 2, BTH, 0x1C,0 }; // MAXBLOKSIZE +//static UCHAR ct29[]={ 2, 0, 0x1D,0 }; // reserved +static UCHAR ct30[] = { 1, INL, 0x1E }; // CTSFLOWENAB +static UCHAR ct31[] = { 1, INL, 0x1F }; // CTSFLOWDSAB +static UCHAR ct32[] = { 1, INL, 0x20 }; // RTSFLOWENAB +static UCHAR ct33[] = { 1, INL, 0x21 }; // RTSFLOWDSAB +static UCHAR ct34[] = { 2, BTH, 0x22,0 }; // ISTRIPMODE +static UCHAR ct35[] = { 2, BTH|END, 0x23,0 }; // SENDBREAK +static UCHAR ct36[] = { 2, BTH, 0x24,0 }; // SETERRMODE +//static UCHAR ct36a[]={ 3, INL, 0x24,0,0 }; // SET_REPLACE + +// The following is listed for completeness, but should never be sent directly +// by user-level code. It is sent only by library routines in response to data +// movement. +//static UCHAR ct37[]={ 5, BYP|VIP, 0x25,0,0,0,0 }; // FLOW PACKET + +// Back to normal +static UCHAR ct38[] = {11, BTH|VAR, 0x26,0,0,0,0,0,0,0,0,0,0 }; // DEF KEY SEQ +//static UCHAR ct39[]={ 3, BTH|END, 0x27,0,0 }; // OPOSTON +//static UCHAR ct40[]={ 1, BTH|END, 0x28 }; // OPOSTOFF +static UCHAR ct41[] = { 1, BYP, 0x29 }; // RESUME +//static UCHAR ct42[]={ 2, BTH, 0x2A,0 }; // TXBAUD +//static UCHAR ct43[]={ 2, BTH, 0x2B,0 }; // RXBAUD +//static UCHAR ct44[]={ 2, BTH, 0x2C,0 }; // MS PING +//static UCHAR ct45[]={ 1, BTH, 0x2D }; // HOTENAB +//static UCHAR ct46[]={ 1, BTH, 0x2E }; // HOTDSAB +static UCHAR ct47[] = { 7, BTH, 0x2F,0,0,0,0,0,0 }; // UNIX FLAGS +//static UCHAR ct48[]={ 1, BTH, 0x30 }; // DSRFLOWENAB +//static UCHAR ct49[]={ 1, BTH, 0x31 }; // DSRFLOWDSAB +//static UCHAR ct50[]={ 1, BTH, 0x32 }; // DTRFLOWENAB +//static UCHAR ct51[]={ 1, BTH, 0x33 }; // DTRFLOWDSAB +//static UCHAR ct52[]={ 1, BTH, 0x34 }; // BAUDTABRESET +static UCHAR ct53[] = { 3, BTH, 0x35,0,0 }; // BAUDREMAP +static UCHAR ct54[] = { 3, BTH, 0x36,0,0 }; // CUSTOMBAUD1 +static UCHAR ct55[] = { 3, BTH, 0x37,0,0 }; // CUSTOMBAUD2 +static UCHAR ct56[] = { 2, BTH|END, 0x38,0 }; // PAUSE +static UCHAR ct57[] = { 1, BYP, 0x39 }; // SUSPEND +static UCHAR ct58[] = { 1, BYP, 0x3A }; // UNSUSPEND +static UCHAR ct59[] = { 2, BTH, 0x3B,0 }; // PARITYCHK +static UCHAR ct60[] = { 1, INL|VIP, 0x3C }; // BOOKMARKREQ +//static UCHAR ct61[]={ 2, BTH, 0x3D,0 }; // INTERNALLOOP +//static UCHAR ct62[]={ 2, BTH, 0x3E,0 }; // HOTKTIMEOUT +static UCHAR ct63[] = { 2, INL, 0x3F,0 }; // SETTXON +static UCHAR ct64[] = { 2, INL, 0x40,0 }; // SETTXOFF +//static UCHAR ct65[]={ 2, BTH, 0x41,0 }; // SETAUTORTS +//static UCHAR ct66[]={ 2, BTH, 0x42,0 }; // SETHIGHWAT +//static UCHAR ct67[]={ 2, BYP, 0x43,0 }; // STARTSELFL +//static UCHAR ct68[]={ 2, INL, 0x44,0 }; // ENDSELFL +//static UCHAR ct69[]={ 1, BYP, 0x45 }; // HWFLOW_OFF +//static UCHAR ct70[]={ 1, BTH, 0x46 }; // ODSRFL_ENAB +//static UCHAR ct71[]={ 1, BTH, 0x47 }; // ODSRFL_DSAB +//static UCHAR ct72[]={ 1, BTH, 0x48 }; // ODCDFL_ENAB +//static UCHAR ct73[]={ 1, BTH, 0x49 }; // ODCDFL_DSAB +//static UCHAR ct74[]={ 2, BTH, 0x4A,0 }; // LOADLEVEL +//static UCHAR ct75[]={ 2, BTH, 0x4B,0 }; // STATDATA +//static UCHAR ct76[]={ 1, BYP, 0x4C }; // BREAK_ON +//static UCHAR ct77[]={ 1, BYP, 0x4D }; // BREAK_OFF +//static UCHAR ct78[]={ 1, BYP, 0x4E }; // GETFC +static UCHAR ct79[] = { 2, BYP, 0x4F,0 }; // XMIT_NOW +//static UCHAR ct80[]={ 4, BTH, 0x50,0,0,0 }; // DIVISOR_LATCH +//static UCHAR ct81[]={ 1, BYP, 0x51 }; // GET_STATUS +//static UCHAR ct82[]={ 1, BYP, 0x52 }; // GET_TXCNT +//static UCHAR ct83[]={ 1, BYP, 0x53 }; // GET_RXCNT +//static UCHAR ct84[]={ 1, BYP, 0x54 }; // GET_BOXIDS +//static UCHAR ct85[]={10, BYP, 0x55,0,0,0,0,0,0,0,0,0 }; // ENAB_MULT +//static UCHAR ct86[]={ 2, BTH, 0x56,0 }; // RCV_ENABLE +static UCHAR ct87[] = { 1, BYP, 0x57 }; // HW_TEST +//static UCHAR ct88[]={ 3, BTH, 0x58,0,0 }; // RCV_THRESHOLD +//static UCHAR ct89[]={ 1, BYP, 0x59 }; // DSS_NOW +//static UCHAR ct90[]={ 3, BYP, 0x5A,0,0 }; // Set SILO +//static UCHAR ct91[]={ 2, BYP, 0x5B,0 }; // timed break + +// Some composite commands as well +//static UCHAR cc01[]={ 2, BTH, 0x02,0x04 }; // DTR & RTS UP +//static UCHAR cc02[]={ 2, BTH, 0x03,0x05 }; // DTR & RTS DN + +//******** +//* Code * +//******** + +//****************************************************************************** +// Function: i2cmdSetSeq(type, size, string) +// Parameters: type - sequence number +// size - length of sequence +// string - substitution string +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 38 Define Hot Key sequence (alias +// "special receive sequence"). Returns a pointer to the structure. Endeavours +// to be bullet-proof in that the sequence number is forced in range, and any +// out-of-range sizes are forced to zero. +//****************************************************************************** +cmdSyntaxPtr +i2cmdSetSeq(unsigned char type, unsigned char size, unsigned char *string) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct38; + unsigned char *pc; + + pCM->cmd[1] = ((type > 0xf) ? 0xf : type); // Sequence number + size = ((size > 0x8) ? 0 : size); // size + pCM->cmd[2] = size; + pCM->length = 3 + size; // UPDATES THE LENGTH! + + pc = &(pCM->cmd[3]); + + while(size--) { + *pc++ = *string++; + } + return pCM; +} + +//****************************************************************************** +// Function: i2cmdUnixFlags(iflag, cflag, lflag) +// Parameters: Unix tty flags +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 47 and returns a pointer to the +// appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdUnixFlags(unsigned short iflag,unsigned short cflag,unsigned short lflag) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct47; + + pCM->cmd[1] = (unsigned char) iflag; + pCM->cmd[2] = (unsigned char) (iflag >> 8); + pCM->cmd[3] = (unsigned char) cflag; + pCM->cmd[4] = (unsigned char) (cflag >> 8); + pCM->cmd[5] = (unsigned char) lflag; + pCM->cmd[6] = (unsigned char) (lflag >> 8); + return pCM; +} + +//****************************************************************************** +// Function: i2cmdBaudRemap(dest,src) +// Parameters: ? +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of command 53 and returns a pointer to the +// appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdBaudRemap(unsigned char dest, unsigned char src) +{ + cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct53; + + pCM->cmd[1] = dest; + pCM->cmd[2] = src; + return pCM; +} + +//****************************************************************************** +// Function: i2cmdBaudDef(which, rate) +// Parameters: ? +// +// Returns: Pointer to command structure +// +// Description: +// +// This routine sets the parameters of commands 54 or 55 (according to the +// argument which), and returns a pointer to the appropriate structure. +//****************************************************************************** +cmdSyntaxPtr +i2cmdBaudDef(int which, unsigned short rate) +{ + cmdSyntaxPtr pCM; + + switch(which) + { + case 1: + pCM = (cmdSyntaxPtr) ct54; + break; + default: + case 2: + pCM = (cmdSyntaxPtr) ct55; + break; + } + pCM->cmd[1] = (unsigned char) rate; + pCM->cmd[2] = (unsigned char) (rate >> 8); + return pCM; +} + diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2cmd.h linux/drivers/char/ip2/i2cmd.h --- v2.2.11/linux/drivers/char/ip2/i2cmd.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2cmd.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,660 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions and support for In-line and Bypass commands. +* Applicable only when the standard loadware is active. +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// Revision History: +// +// 10 October 1991 MAG First Draft +// 7 November 1991 MAG Reflects some new commands +// 20 February 1992 MAG CMD_HOTACK corrected: no argument. +// 24 February 1992 MAG Support added for new commands for 1.4.x loadware. +// 11 March 1992 MAG Additional commands. +// 16 March 1992 MAG Additional commands. +// 30 March 1992 MAG Additional command: CMD_DSS_NOW +// 18 May 1992 MAG Changed CMD_OPOST +// +//------------------------------------------------------------------------------ +#ifndef I2CMD_H // To prevent multiple includes +#define I2CMD_H 1 + +#include "ip2types.h" + +// This module is designed to provide a uniform method of sending commands to +// the board through command packets. The difficulty is, some commands take +// parameters, others do not. Furthermore, it is often useful to send several +// commands to the same channel as part of the same packet. (See also i2pack.h.) +// +// This module is designed so that the caller should not be responsible for +// remembering the exact syntax of each command, or at least so that the +// compiler could check things somewhat. I'll explain as we go... +// +// First, a structure which can embody the syntax of each type of command. +// +typedef struct _cmdSyntax +{ + UCHAR length; // Number of bytes in the command + UCHAR flags; // Information about the command (see below) + + // The command and its parameters, which may be of arbitrary length. Don't + // worry yet how the parameters will be initialized; macros later take care + // of it. Also, don't worry about the arbitrary length issue; this structure + // is never used to allocate space (see i2cmd.c). + UCHAR cmd[2]; +} cmdSyntax, *cmdSyntaxPtr; + +// Bit assignments for flags + +#define INL 1 // Set if suitable for inline commands +#define BYP 2 // Set if suitable for bypass commands +#define BTH (INL|BYP) // suitable for either! +#define END 4 // Set if this must be the last command in a block +#define VIP 8 // Set if this command is special in some way and really + // should only be sent from the library-level and not + // directly from user-level +#define VAR 0x10 // This command is of variable length! + +//----------------------------------- +// External declarations for i2cmd.c +//----------------------------------- +// Routine to set up parameters for the "define hot-key sequence" command. Since +// there is more than one parameter to assign, we must use a function rather +// than a macro (used usually). +// +extern cmdSyntaxPtr i2cmdSetSeq(UCHAR seqno, UCHAR size, UCHAR *string); +extern cmdSyntaxPtr i2cmdUnixFlags(USHORT iflag,USHORT cflag,USHORT lflag); +extern cmdSyntaxPtr i2cmdBaudRemap(UCHAR dest, UCHAR src); +extern cmdSyntaxPtr i2cmdBaudDef(int which, USHORT rate); + +// Declarations for the global arrays used to bear the commands and their +// arguments. +// +// Note: Since these are globals and the arguments might change, it is important +// that the library routine COPY these into buffers from whence they would be +// sent, rather than merely storing the pointers. In multi-threaded +// environments, important that the copy should obtain before any context switch +// is allowed. Also, for parameterized commands, DO NOT ISSUE THE SAME COMMAND +// MORE THAN ONCE WITH THE SAME PARAMETERS in the same call. +// +static UCHAR ct02[]; +static UCHAR ct03[]; +static UCHAR ct04[]; +static UCHAR ct05[]; +static UCHAR ct06[]; +static UCHAR ct07[]; +static UCHAR ct08[]; +static UCHAR ct09[]; +static UCHAR ct10[]; +static UCHAR ct11[]; +static UCHAR ct12[]; +static UCHAR ct13[]; +static UCHAR ct14[]; +static UCHAR ct15[]; +static UCHAR ct16[]; +static UCHAR ct17[]; +static UCHAR ct18[]; +static UCHAR ct19[]; +static UCHAR ct20[]; +static UCHAR ct21[]; +static UCHAR ct22[]; +static UCHAR ct23[]; +static UCHAR ct24[]; +static UCHAR ct25[]; +static UCHAR ct26[]; +static UCHAR ct27[]; +static UCHAR ct28[]; +static UCHAR ct29[]; +static UCHAR ct30[]; +static UCHAR ct31[]; +static UCHAR ct32[]; +static UCHAR ct33[]; +static UCHAR ct34[]; +static UCHAR ct35[]; +static UCHAR ct36[]; +static UCHAR ct36a[]; +static UCHAR ct41[]; +static UCHAR ct42[]; +static UCHAR ct43[]; +static UCHAR ct44[]; +static UCHAR ct45[]; +static UCHAR ct46[]; +static UCHAR ct48[]; +static UCHAR ct49[]; +static UCHAR ct50[]; +static UCHAR ct51[]; +static UCHAR ct52[]; +static UCHAR ct56[]; +static UCHAR ct57[]; +static UCHAR ct58[]; +static UCHAR ct59[]; +static UCHAR ct60[]; +static UCHAR ct61[]; +static UCHAR ct62[]; +static UCHAR ct63[]; +static UCHAR ct64[]; +static UCHAR ct65[]; +static UCHAR ct66[]; +static UCHAR ct67[]; +static UCHAR ct68[]; +static UCHAR ct69[]; +static UCHAR ct70[]; +static UCHAR ct71[]; +static UCHAR ct72[]; +static UCHAR ct73[]; +static UCHAR ct74[]; +static UCHAR ct75[]; +static UCHAR ct76[]; +static UCHAR ct77[]; +static UCHAR ct78[]; +static UCHAR ct79[]; +static UCHAR ct80[]; +static UCHAR ct81[]; +static UCHAR ct82[]; +static UCHAR ct83[]; +static UCHAR ct84[]; +static UCHAR ct85[]; +static UCHAR ct86[]; +static UCHAR ct87[]; +static UCHAR ct88[]; +static UCHAR ct89[]; +static UCHAR ct90[]; +static UCHAR ct91[]; +static UCHAR cc01[]; +static UCHAR cc02[]; + +// Now, refer to i2cmd.c, and see the character arrays defined there. They are +// cast here to cmdSyntaxPtr. +// +// There are library functions for issuing bypass or inline commands. These +// functions take one or more arguments of the type cmdSyntaxPtr. The routine +// then can figure out how long each command is supposed to be and easily add it +// to the list. +// +// For ease of use, we define manifests which return pointers to appropriate +// cmdSyntaxPtr things. But some commands also take arguments. If a single +// argument is used, we define a macro which performs the single assignment and +// (through the expedient of a comma expression) references the appropriate +// pointer. For commands requiring several arguments, we actually define a +// function to perform the assignments. + +#define CMD_DTRUP (cmdSyntaxPtr)(ct02) // Raise DTR +#define CMD_DTRDN (cmdSyntaxPtr)(ct03) // Lower DTR +#define CMD_RTSUP (cmdSyntaxPtr)(ct04) // Raise RTS +#define CMD_RTSDN (cmdSyntaxPtr)(ct05) // Lower RTS +#define CMD_STARTFL (cmdSyntaxPtr)(ct06) // Start Flushing Data + +#define CMD_DTRRTS_UP (cmdSyntaxPtr)(cc01) // Raise DTR and RTS +#define CMD_DTRRTS_DN (cmdSyntaxPtr)(cc02) // Lower DTR and RTS + +// Set Baud Rate for transmit and receive +#define CMD_SETBAUD(arg) \ + (((cmdSyntaxPtr)(ct07))->cmd[1] = (arg),(cmdSyntaxPtr)(ct07)) + +#define CBR_50 1 +#define CBR_75 2 +#define CBR_110 3 +#define CBR_134 4 +#define CBR_150 5 +#define CBR_200 6 +#define CBR_300 7 +#define CBR_600 8 +#define CBR_1200 9 +#define CBR_1800 10 +#define CBR_2400 11 +#define CBR_4800 12 +#define CBR_9600 13 +#define CBR_19200 14 +#define CBR_38400 15 +#define CBR_2000 16 +#define CBR_3600 17 +#define CBR_7200 18 +#define CBR_56000 19 +#define CBR_57600 20 +#define CBR_64000 21 +#define CBR_76800 22 +#define CBR_115200 23 +#define CBR_C1 24 // Custom baud rate 1 +#define CBR_C2 25 // Custom baud rate 2 +#define CBR_153600 26 +#define CBR_230400 27 +#define CBR_307200 28 +#define CBR_460800 29 +#define CBR_921600 30 + +// Set Character size +// +#define CMD_SETBITS(arg) \ + (((cmdSyntaxPtr)(ct08))->cmd[1] = (arg),(cmdSyntaxPtr)(ct08)) + +#define CSZ_5 0 +#define CSZ_6 1 +#define CSZ_7 2 +#define CSZ_8 3 + +// Set number of stop bits +// +#define CMD_SETSTOP(arg) \ + (((cmdSyntaxPtr)(ct09))->cmd[1] = (arg),(cmdSyntaxPtr)(ct09)) + +#define CST_1 0 +#define CST_15 1 // 1.5 stop bits +#define CST_2 2 + +// Set parity option +// +#define CMD_SETPAR(arg) \ + (((cmdSyntaxPtr)(ct10))->cmd[1] = (arg),(cmdSyntaxPtr)(ct10)) + +#define CSP_NP 0 // no parity +#define CSP_OD 1 // odd parity +#define CSP_EV 2 // Even parity +#define CSP_SP 3 // Space parity +#define CSP_MK 4 // Mark parity + +// Define xon char for transmitter flow control +// +#define CMD_DEF_IXON(arg) \ + (((cmdSyntaxPtr)(ct11))->cmd[1] = (arg),(cmdSyntaxPtr)(ct11)) + +// Define xoff char for transmitter flow control +// +#define CMD_DEF_IXOFF(arg) \ + (((cmdSyntaxPtr)(ct12))->cmd[1] = (arg),(cmdSyntaxPtr)(ct12)) + +#define CMD_STOPFL (cmdSyntaxPtr)(ct13) // Stop Flushing data + +// Acknowledge receipt of hotkey signal +// +#define CMD_HOTACK (cmdSyntaxPtr)(ct14) + +// Define irq level to use. Should actually be sent by library-level code, not +// directly from user... +// +#define CMDVALUE_IRQ 15 // For library use at initialization. Until this command + // is sent, board processing doesn't really start. +#define CMD_SET_IRQ(arg) \ + (((cmdSyntaxPtr)(ct15))->cmd[1] = (arg),(cmdSyntaxPtr)(ct15)) + +#define CIR_POLL 0 // No IRQ - Poll +#define CIR_3 3 // IRQ 3 +#define CIR_4 4 // IRQ 4 +#define CIR_5 5 // IRQ 5 +#define CIR_7 7 // IRQ 7 +#define CIR_10 10 // IRQ 10 +#define CIR_11 11 // IRQ 11 +#define CIR_12 12 // IRQ 12 +#define CIR_15 15 // IRQ 15 + +// Select transmit flow xon/xoff options +// +#define CMD_IXON_OPT(arg) \ + (((cmdSyntaxPtr)(ct16))->cmd[1] = (arg),(cmdSyntaxPtr)(ct16)) + +#define CIX_NONE 0 // Incoming Xon/Xoff characters not special +#define CIX_XON 1 // Xoff disable, Xon enable +#define CIX_XANY 2 // Xoff disable, any key enable + +// Select receive flow xon/xoff options +// +#define CMD_OXON_OPT(arg) \ + (((cmdSyntaxPtr)(ct17))->cmd[1] = (arg),(cmdSyntaxPtr)(ct17)) + +#define COX_NONE 0 // Don't send Xon/Xoff +#define COX_XON 1 // Send xon/xoff to start/stop incoming data + + +#define CMD_CTS_REP (cmdSyntaxPtr)(ct18) // Enable CTS reporting +#define CMD_CTS_NREP (cmdSyntaxPtr)(ct19) // Disable CTS reporting + +#define CMD_DCD_REP (cmdSyntaxPtr)(ct20) // Enable DCD reporting +#define CMD_DCD_NREP (cmdSyntaxPtr)(ct21) // Disable DCD reporting + +#define CMD_DSR_REP (cmdSyntaxPtr)(ct22) // Enable DSR reporting +#define CMD_DSR_NREP (cmdSyntaxPtr)(ct23) // Disable DSR reporting + +#define CMD_RI_REP (cmdSyntaxPtr)(ct24) // Enable RI reporting +#define CMD_RI_NREP (cmdSyntaxPtr)(ct25) // Disable RI reporting + +// Enable break reporting and select style +// +#define CMD_BRK_REP(arg) \ + (((cmdSyntaxPtr)(ct26))->cmd[1] = (arg),(cmdSyntaxPtr)(ct26)) + +#define CBK_STAT 0x00 // Report breaks as a status (exception,irq) +#define CBK_NULL 0x01 // Report breaks as a good null +#define CBK_STAT_SEQ 0x02 // Report breaks as a status AND as in-band character + // sequence FFh, 01h, 10h +#define CBK_SEQ 0x03 // Report breaks as the in-band + //sequence FFh, 01h, 10h ONLY. +#define CBK_FLSH 0x04 // if this bit set also flush input data +#define CBK_POSIX 0x08 // if this bit set report as FF,0,0 sequence +#define CBK_SINGLE 0x10 // if this bit set with CBK_SEQ or CBK_STAT_SEQ + //then reports single null instead of triple + +#define CMD_BRK_NREP (cmdSyntaxPtr)(ct27) // Disable break reporting + +// Specify maximum block size for received data +// +#define CMD_MAX_BLOCK(arg) \ + (((cmdSyntaxPtr)(ct28))->cmd[1] = (arg),(cmdSyntaxPtr)(ct28)) + +// -- COMMAND 29 is reserved -- + +#define CMD_CTSFL_ENAB (cmdSyntaxPtr)(ct30) // Enable CTS flow control +#define CMD_CTSFL_DSAB (cmdSyntaxPtr)(ct31) // Disable CTS flow control +#define CMD_RTSFL_ENAB (cmdSyntaxPtr)(ct32) // Enable RTS flow control +#define CMD_RTSFL_DSAB (cmdSyntaxPtr)(ct33) // Disable RTS flow control + +// Specify istrip option +// +#define CMD_ISTRIP_OPT(arg) \ + (((cmdSyntaxPtr)(ct34))->cmd[1] = (arg),(cmdSyntaxPtr)(ct34)) + +#define CIS_NOSTRIP 0 // Strip characters to character size +#define CIS_STRIP 1 // Strip any 8-bit characters to 7 bits + +// Send a break of arg milliseconds +// +#define CMD_SEND_BRK(arg) \ + (((cmdSyntaxPtr)(ct35))->cmd[1] = (arg),(cmdSyntaxPtr)(ct35)) + +// Set error reporting mode +// +#define CMD_SET_ERROR(arg) \ + (((cmdSyntaxPtr)(ct36))->cmd[1] = (arg),(cmdSyntaxPtr)(ct36)) + +#define CSE_ESTAT 0 // Report error in a status packet +#define CSE_NOREP 1 // Treat character as though it were good +#define CSE_DROP 2 // Discard the character +#define CSE_NULL 3 // Replace with a null +#define CSE_MARK 4 // Replace with a 3-character sequence (as Unix) + +#define CMD_SET_REPLACEMENT(arg,ch) \ + (((cmdSyntaxPtr)(ct36a))->cmd[1] = (arg), \ + (((cmdSyntaxPtr)(ct36a))->cmd[2] = (ch), \ + (cmdSyntaxPtr)(ct36a)) + +#define CSE_REPLACE 0x8 // Replace the errored character with the + // replacement character defined here + +#define CSE_STAT_REPLACE 0x18 // Replace the errored character with the + // replacement character defined here AND + // report the error as a status packet (as in + // CSE_ESTAT). + + +// COMMAND 37, to send flow control packets, is handled only by low-level +// library code in response to data movement and shouldn't ever be sent by the +// user code. See i2pack.h and the body of i2lib.c for details. + +// COMMAND 38: Define the hot-key sequence +// seqno: sequence number 0-15 +// size: number of characters in sequence (1-8) +// string: pointer to the characters +// (if size == 0, "undefines" this sequence +// +#define CMD_SET_SEQ(seqno,size,string) i2cmdSetSeq(seqno,size,string) + +// Enable on-board post-processing, using options given in oflag argument. +// Formerly, this command was automatically preceded by a CMD_OPOST_OFF command +// because the loadware does not permit sending back-to-back CMD_OPOST_ON +// commands without an intervening CMD_OPOST_OFF. BUT, WE LEARN 18 MAY 92, that +// CMD_OPOST_ON and CMD_OPOST_OFF must each be at the end of a packet (or in a +// solo packet). This means the caller must specify separately CMD_OPOST_OFF, +// CMD_OPOST_ON(parm) when he calls i2QueueCommands(). That function will ensure +// each gets a separate packet. Extra CMD_OPOST_OFF's are always ok. +// +#define CMD_OPOST_ON(oflag) \ + (*(USHORT *)(((cmdSyntaxPtr)(ct39))->cmd[1]) = (oflag), \ + (cmdSyntaxPtr)(ct39)) + +#define CMD_OPOST_OFF (cmdSyntaxPtr)(ct40) // Disable on-board post-proc + +#define CMD_RESUME (cmdSyntaxPtr)(ct41) // Resume: behave as though an XON + // were received; + +// Set Transmit baud rate (see command 7 for arguments) +// +#define CMD_SETBAUD_TX(arg) \ + (((cmdSyntaxPtr)(ct42))->cmd[1] = (arg),(cmdSyntaxPtr)(ct42)) + +// Set Receive baud rate (see command 7 for arguments) +// +#define CMD_SETBAUD_RX(arg) \ + (((cmdSyntaxPtr)(ct43))->cmd[1] = (arg),(cmdSyntaxPtr)(ct43)) + +// Request interrupt from board each arg milliseconds. Interrupt will specify +// "received data", even though there may be no data present. If arg == 0, +// disables any such interrupts. +// +#define CMD_PING_REQ(arg) \ + (((cmdSyntaxPtr)(ct44))->cmd[1] = (arg),(cmdSyntaxPtr)(ct44)) + +#define CMD_HOT_ENAB (cmdSyntaxPtr)(ct45) // Enable Hot-key checking +#define CMD_HOT_DSAB (cmdSyntaxPtr)(ct46) // Disable Hot-key checking + +// COMMAND 47: Send Protocol info via Unix flags: +// iflag = Unix tty t_iflag +// cflag = Unix tty t_cflag +// lflag = Unix tty t_lflag +// See System V Unix/Xenix documentation for the meanings of the bit fields +// within these flags +// +#define CMD_UNIX_FLAGS(iflag,cflag,lflag) i2cmdUnixFlags(iflag,cflag,lflag) + +#define CMD_DSRFL_ENAB (cmdSyntaxPtr)(ct48) // Enable DSR receiver ctrl +#define CMD_DSRFL_DSAB (cmdSyntaxPtr)(ct49) // Disable DSR receiver ctrl +#define CMD_DTRFL_ENAB (cmdSyntaxPtr)(ct50) // Enable DTR flow control +#define CMD_DTRFL_DSAB (cmdSyntaxPtr)(ct51) // Disable DTR flow control +#define CMD_BAUD_RESET (cmdSyntaxPtr)(ct52) // Reset baudrate table + +// COMMAND 53: Remap baud rate table +// dest = index of table entry to be changed +// src = index value to substitute. +// at default mapping table is f(x) = x +// +#define CMD_BAUD_REMAP(dest,src) i2cmdBaudRemap(dest,src) + +// COMMAND 54: Define custom rate #1 +// rate = (short) 1/10 of the desired baud rate +// +#define CMD_BAUD_DEF1(rate) i2cmdBaudDef(1,rate) + +// COMMAND 55: Define custom rate #2 +// rate = (short) 1/10 of the desired baud rate +// +#define CMD_BAUD_DEF2(rate) i2cmdBaudDef(2,rate) + +// Pause arg hundredths of seconds. (Note, this is NOT milliseconds.) +// +#define CMD_PAUSE(arg) \ + (((cmdSyntaxPtr)(ct56))->cmd[1] = (arg),(cmdSyntaxPtr)(ct56)) + +#define CMD_SUSPEND (cmdSyntaxPtr)(ct57) // Suspend output +#define CMD_UNSUSPEND (cmdSyntaxPtr)(ct58) // Un-Suspend output + +// Set parity-checking options +// +#define CMD_PARCHK(arg) \ + (((cmdSyntaxPtr)(ct59))->cmd[1] = (arg),(cmdSyntaxPtr)(ct59)) + +#define CPK_ENAB 0 // Enable parity checking on input +#define CPK_DSAB 1 // Disable parity checking on input + +#define CMD_BMARK_REQ (cmdSyntaxPtr)(ct60) // Bookmark request + + +// Enable/Disable internal loopback mode +// +#define CMD_INLOOP(arg) \ + (((cmdSyntaxPtr)(ct61))->cmd[1] = (arg),(cmdSyntaxPtr)(ct61)) + +#define CIN_DISABLE 0 // Normal operation (default) +#define CIN_ENABLE 1 // Internal (local) loopback +#define CIN_REMOTE 2 // Remote loopback + +// Specify timeout for hotkeys: Delay will be (arg x 10) milliseconds, arg == 0 +// --> no timeout: wait forever. +// +#define CMD_HOT_TIME(arg) \ + (((cmdSyntaxPtr)(ct62))->cmd[1] = (arg),(cmdSyntaxPtr)(ct62)) + + +// Define (outgoing) xon for receive flow control +// +#define CMD_DEF_OXON(arg) \ + (((cmdSyntaxPtr)(ct63))->cmd[1] = (arg),(cmdSyntaxPtr)(ct63)) + +// Define (outgoing) xoff for receiver flow control +// +#define CMD_DEF_OXOFF(arg) \ + (((cmdSyntaxPtr)(ct64))->cmd[1] = (arg),(cmdSyntaxPtr)(ct64)) + +// Enable/Disable RTS on transmit (1/2 duplex-style) +// +#define CMD_RTS_XMIT(arg) \ + (((cmdSyntaxPtr)(ct65))->cmd[1] = (arg),(cmdSyntaxPtr)(ct65)) + +#define CHD_DISABLE 0 +#define CHD_ENABLE 1 + +// Set high-water-mark level (debugging use only) +// +#define CMD_SETHIGHWAT(arg) \ + (((cmdSyntaxPtr)(ct66))->cmd[1] = (arg),(cmdSyntaxPtr)(ct66)) + +// Start flushing tagged data (tag = 0-14) +// +#define CMD_START_SELFL(tag) \ + (((cmdSyntaxPtr)(ct67))->cmd[1] = (tag),(cmdSyntaxPtr)(ct67)) + +// End flushing tagged data (tag = 0-14) +// +#define CMD_END_SELFL(tag) \ + (((cmdSyntaxPtr)(ct68))->cmd[1] = (tag),(cmdSyntaxPtr)(ct68)) + +#define CMD_HWFLOW_OFF (cmdSyntaxPtr)(ct69) // Disable HW TX flow control +#define CMD_ODSRFL_ENAB (cmdSyntaxPtr)(ct70) // Enable DSR output f/c +#define CMD_ODSRFL_DSAB (cmdSyntaxPtr)(ct71) // Disable DSR output f/c +#define CMD_ODCDFL_ENAB (cmdSyntaxPtr)(ct72) // Enable DCD output f/c +#define CMD_ODCDFL_DSAB (cmdSyntaxPtr)(ct73) // Disable DCD output f/c + +// Set transmit interrupt load level. Count should be an even value 2-12 +// +#define CMD_LOADLEVEL(count) \ + (((cmdSyntaxPtr)(ct74))->cmd[1] = (count),(cmdSyntaxPtr)(ct74)) + +// If reporting DSS changes, map to character sequence FFh, 2, MSR +// +#define CMD_STATDATA(arg) \ + (((cmdSyntaxPtr)(ct75))->cmd[1] = (arg),(cmdSyntaxPtr)(ct75)) + +#define CSTD_DISABLE// Report DSS changes as status packets only (default) +#define CSTD_ENABLE // Report DSS changes as in-band data sequence as well as + // by status packet. + +#define CMD_BREAK_ON (cmdSyntaxPtr)(ct76)// Set break and stop xmit +#define CMD_BREAK_OFF (cmdSyntaxPtr)(ct77)// End break and restart xmit +#define CMD_GETFC (cmdSyntaxPtr)(ct78)// Request for flow control packet + // from board. + +// Transmit this character immediately +// +#define CMD_XMIT_NOW(ch) \ + (((cmdSyntaxPtr)(ct79))->cmd[1] = (ch),(cmdSyntaxPtr)(ct79)) + +// Set baud rate via "divisor latch" +// +#define CMD_DIVISOR_LATCH(which,value) \ + (((cmdSyntaxPtr)(ct80))->cmd[1] = (which), \ + *(USHORT *)(((cmdSyntaxPtr)(ct80))->cmd[2]) = (value), \ + (cmdSyntaxPtr)(ct80)) + +#define CDL_RX 1 // Set receiver rate +#define CDL_TX 2 // Set transmit rate + // (CDL_TX | CDL_RX) Set both rates + +// Request for special diagnostic status pkt from the board. +// +#define CMD_GET_STATUS (cmdSyntaxPtr)(ct81) + +// Request time-stamped transmit character count packet. +// +#define CMD_GET_TXCNT (cmdSyntaxPtr)(ct82) + +// Request time-stamped receive character count packet. +// +#define CMD_GET_RXCNT (cmdSyntaxPtr)(ct83) + +// Request for box/board I.D. packet. +#define CMD_GET_BOXIDS (cmdSyntaxPtr)(ct84) + +// Enable or disable multiple channels according to bit-mapped ushorts box 1-4 +// +#define CMD_ENAB_MULT(enable, box1, box2, box3, box4) \ + (((cmdSytaxPtr)(ct85))->cmd[1] = (enable), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[2]) = (box1), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[4]) = (box2), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[6]) = (box3), \ + *(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[8]) = (box4), \ + (cmdSyntaxPtr)(ct85)) + +#define CEM_DISABLE 0 +#define CEM_ENABLE 1 + +// Enable or disable receiver or receiver interrupts (default both enabled) +// +#define CMD_RCV_ENABLE(ch) \ + (((cmdSyntaxPtr)(ct86))->cmd[1] = (ch),(cmdSyntaxPtr)(ct86)) + +#define CRE_OFF 0 // Disable the receiver +#define CRE_ON 1 // Enable the receiver +#define CRE_INTOFF 2 // Disable receiver interrupts (to loadware) +#define CRE_INTON 3 // Enable receiver interrupts (to loadware) + +// Starts up a hardware test process, which runs transparently, and sends a +// STAT_HWFAIL packet in case a hardware failure is detected. +// +#define CMD_HW_TEST (cmdSyntaxPtr)(ct87) + +// Change receiver threshold and timeout value: +// Defaults: timeout = 20mS +// threshold count = 8 when DTRflow not in use, +// threshold count = 5 when DTRflow in use. +// +#define CMD_RCV_THRESHOLD(count,ms) \ + (((cmdSyntaxPtr)(ct88))->cmd[1] = (count), \ + ((cmdSyntaxPtr)(ct88))->cmd[2] = (ms), \ + (cmdSyntaxPtr)(ct88)) + +// Makes the loadware report DSS signals for this channel immediately. +// +#define CMD_DSS_NOW (cmdSyntaxPtr)(ct89) + +// Set the receive silo parameters +// timeout is ms idle wait until delivery (~VTIME) +// threshold is max characters cause interrupt (~VMIN) +// +#define CMD_SET_SILO(timeout,threshold) \ + (((cmdSyntaxPtr)(ct90))->cmd[1] = (timeout), \ + ((cmdSyntaxPtr)(ct90))->cmd[2] = (threshold), \ + (cmdSyntaxPtr)(ct90)) + +// Set timed break in decisecond (1/10s) +// +#define CMD_LBREAK(ds) \ + (((cmdSyntaxPtr)(ct91))->cmd[1] = (ds),(cmdSyntaxPtr)(ct66)) + + + +#endif // I2CMD_H diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2ellis.c linux/drivers/char/ip2/i2ellis.c --- v2.2.11/linux/drivers/char/ip2/i2ellis.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2ellis.c Wed Aug 25 17:29:47 1999 @@ -0,0 +1,1470 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Low-level interface code for the device driver +* (This is included source code, not a separate compilation +* module.) +* +*******************************************************************************/ +//--------------------------------------------- +// Function declarations private to this module +//--------------------------------------------- +// Functions called only indirectly through i2eBordStr entries. + +static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int); +static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int); +static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int); +static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int); + +static unsigned short iiReadWord16(i2eBordStrPtr); +static unsigned short iiReadWord8(i2eBordStrPtr); +static void iiWriteWord16(i2eBordStrPtr, unsigned short); +static void iiWriteWord8(i2eBordStrPtr, unsigned short); + +static int iiWaitForTxEmptyII(i2eBordStrPtr, int); +static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int); +static int iiTxMailEmptyII(i2eBordStrPtr); +static int iiTxMailEmptyIIEX(i2eBordStrPtr); +static int iiTrySendMailII(i2eBordStrPtr, unsigned char); +static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char); + +static unsigned short iiGetMailII(i2eBordStrPtr); +static unsigned short iiGetMailIIEX(i2eBordStrPtr); + +static void iiEnableMailIrqII(i2eBordStrPtr); +static void iiEnableMailIrqIIEX(i2eBordStrPtr); +static void iiWriteMaskII(i2eBordStrPtr, unsigned char); +static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char); + +static void ii2DelayTimer(unsigned int); +static void ii2DelayWakeup(unsigned long id); +static void ii2Nop(void); + +//*************** +//* Static Data * +//*************** + +static int ii2Safe = 0; // Safe I/O address for delay routine + +static int iiDelayed = 0; // Set when the iiResetDelay function is + // called. Cleared when ANY board is reset. +static struct timer_list * pDelayTimer; // Used by iiDelayTimer +static struct wait_queue * pDelayWait; // Used by iiDelayTimer +static spinlock_t Dl_spinlock; + +//******** +//* Code * +//******** + +//======================================================= +// Initialization Routines +// +// iiSetAddress +// iiReset +// iiResetDelay +// iiInitialize +//======================================================= + +//****************************************************************************** +// Function: iiEllisInit() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// This routine performs any required initialization of the iiEllis subsystem. +// +//****************************************************************************** +static void +iiEllisInit(void) +{ + pDelayTimer = kmalloc ( sizeof (struct timer_list), GFP_KERNEL ); + pDelayWait = NULL; + LOCK_INIT(&Dl_spinlock); +} + +//****************************************************************************** +// Function: iiEllisCleanup() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// This routine performs any required cleanup of the iiEllis subsystem. +// +//****************************************************************************** +static void +iiEllisCleanup(void) +{ + if ( pDelayTimer != NULL ) { + kfree ( pDelayTimer ); + } +} + +//****************************************************************************** +// Function: iiSetAddress(pB, address, delay) +// Parameters: pB - pointer to the board structure +// address - the purported I/O address of the board +// delay - pointer to the 1-ms delay function to use +// in this and any future operations to this board +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// This routine (roughly) checks for address validity, sets the i2eValid OK and +// sets the state to II_STATE_COLD which means that we haven't even sent a reset +// yet. +// +//****************************************************************************** +static int +iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay ) +{ + // Should any failure occur before init is finished... + pB->i2eValid = I2E_INCOMPLETE; + + // Cannot check upper limit except extremely: Might be microchannel + // Address must be on an 8-byte boundary + + if ((unsigned int)address <= 0x100 + || (unsigned int)address >= 0xfff8 + || (address & 0x7) + ) + { + COMPLETE(pB,I2EE_BADADDR); + } + + // Initialize accelerators + pB->i2eBase = address; + pB->i2eData = address + FIFO_DATA; + pB->i2eStatus = address + FIFO_STATUS; + pB->i2ePointer = address + FIFO_PTR; + pB->i2eXMail = address + FIFO_MAIL; + pB->i2eXMask = address + FIFO_MASK; + + // Initialize i/o address for ii2DelayIO + ii2Safe = address + FIFO_NOP; + + // Initialize the delay routine + pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop); + + pB->i2eValid = I2E_MAGIC; + pB->i2eState = II_STATE_COLD; + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReset(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Attempts to reset the board (see also i2hw.h). Normally, we would use this to +// reset a board immediately after iiSetAddress(), but it is valid to reset a +// board from any state, say, in order to change or re-load loadware. (Under +// such circumstances, no reason to re-run iiSetAddress(), which is why it is a +// separate routine and not included in this routine. +// +//****************************************************************************** +static int +iiReset(i2eBordStrPtr pB) +{ + // Magic number should be set, else even the address is suspect + if (pB->i2eValid != I2E_MAGIC) + { + COMPLETE(pB, I2EE_BADMAGIC); + } + + OUTB(pB->i2eBase + FIFO_RESET, 0); // Any data will do + iiDelay(pB, 50); // Pause between resets + OUTB(pB->i2eBase + FIFO_RESET, 0); // Second reset + + // We must wait before even attempting to read anything from the FIFO: the + // board's P.O.S.T may actually attempt to read and write its end of the + // FIFO in order to check flags, loop back (where supported), etc. On + // completion of this testing it would reset the FIFO, and on completion + // of all // P.O.S.T., write the message. We must not mistake data which + // might have been sent for testing as part of the reset message. To + // better utilize time, say, when resetting several boards, we allow the + // delay to be performed externally; in this way the caller can reset + // several boards, delay a single time, then call the initialization + // routine for all. + + pB->i2eState = II_STATE_RESET; + + iiDelayed = 0; // i.e., the delay routine hasn't been called since the most + // recent reset. + + // Ensure anything which would have been of use to standard loadware is + // blanked out, since board has now forgotten everything!. + + pB->i2eUsingIrq = IRQ_UNDEFINED; // Not set up to use an interrupt yet + pB->i2eWaitingForEmptyFifo = 0; + pB->i2eOutMailWaiting = 0; + pB->i2eChannelPtr = NULL; + pB->i2eChannelCnt = 0; + + pB->i2eLeadoffWord[0] = 0; + pB->i2eFifoInInts = 0; + pB->i2eFifoOutInts = 0; + pB->i2eFatalTrap = NULL; + pB->i2eFatal = 0; + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiResetDelay(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Using the delay defined in board structure, waits two seconds (for board to +// reset). +// +//****************************************************************************** +static int +iiResetDelay(i2eBordStrPtr pB) +{ + if (pB->i2eValid != I2E_MAGIC) { + COMPLETE(pB, I2EE_BADMAGIC); + } + if (pB->i2eState != II_STATE_RESET) { + COMPLETE(pB, I2EE_BADSTATE); + } + iiDelay(pB,2000); /* Now we wait for two seconds. */ + iiDelayed = 1; /* Delay has been called: ok to initialize */ + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiInitialize(pB) +// Parameters: pB - pointer to the board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Attempts to read the Power-on reset message. Initializes any remaining fields +// in the pB structure. +// +// This should be called as the third step of a process beginning with +// iiReset(), then iiResetDelay(). This routine checks to see that the structure +// is "valid" and in the reset state, also confirms that the delay routine has +// been called since the latest reset (to any board! overly strong!). +// +//****************************************************************************** +static int +iiInitialize(i2eBordStrPtr pB) +{ + int itemp; + unsigned char c; + unsigned short utemp; + unsigned int ilimit; + + if (pB->i2eValid != I2E_MAGIC) + { + COMPLETE(pB, I2EE_BADMAGIC); + } + + if (pB->i2eState != II_STATE_RESET || !iiDelayed) + { + COMPLETE(pB, I2EE_BADSTATE); + } + + // In case there is a failure short of our completely reading the power-up + // message. + pB->i2eValid = I2E_INCOMPLETE; + + + // Now attempt to read the message. + + for (itemp = 0; itemp < sizeof(porStr); itemp++) + { + // We expect the entire message is ready. + if (HAS_NO_INPUT(pB)) + { + pB->i2ePomSize = itemp; + COMPLETE(pB, I2EE_PORM_SHORT); + } + + pB->i2ePom.c[itemp] = c = BYTE_FROM(pB); + + // We check the magic numbers as soon as they are supposed to be read + // (rather than after) to minimize effect of reading something we + // already suspect can't be "us". + if ( (itemp == POR_1_INDEX && c != POR_MAGIC_1) || + (itemp == POR_2_INDEX && c != POR_MAGIC_2)) + { + pB->i2ePomSize = itemp+1; + COMPLETE(pB, I2EE_BADMAGIC); + } + } + + pB->i2ePomSize = itemp; + + // Ensure that this was all the data... + if (HAS_INPUT(pB)) + COMPLETE(pB, I2EE_PORM_LONG); + + // For now, we'll fail to initialize if P.O.S.T reports bad chip mapper: + // Implying we will not be able to download any code either: That's ok: the + // condition is pretty explicit. + if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER) + { + COMPLETE(pB, I2EE_POSTERR); + } + + // Determine anything which must be done differently depending on the family + // of boards! + switch (pB->i2ePom.e.porID & POR_ID_FAMILY) + { + case POR_ID_FII: // IntelliPort-II + + pB->i2eFifoStyle = FIFO_II; + pB->i2eFifoSize = 512; // 512 bytes, always + pB->i2eDataWidth16 = NO; + + pB->i2eMaxIrq = 15; // Because board cannot tell us it is in an 8-bit + // slot, we do allow it to be done (documentation!) + + pB->i2eGoodMap[1] = + pB->i2eGoodMap[2] = + pB->i2eGoodMap[3] = + pB->i2eChannelMap[1] = + pB->i2eChannelMap[2] = + pB->i2eChannelMap[3] = 0; + + switch (pB->i2ePom.e.porID & POR_ID_SIZE) + { + case POR_ID_II_4: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0x0f; // four-port + + // Since porPorts1 is based on the Hardware ID register, the numbers + // should always be consistent for IntelliPort-II. Ditto below... + if (pB->i2ePom.e.porPorts1 != 4) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + + case POR_ID_II_8: + case POR_ID_II_8R: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0xff; // Eight port + if (pB->i2ePom.e.porPorts1 != 8) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + + case POR_ID_II_6: + pB->i2eGoodMap[0] = + pB->i2eChannelMap[0] = 0x3f; // Six Port + if (pB->i2ePom.e.porPorts1 != 6) + { + COMPLETE(pB, I2EE_INCONSIST); + } + break; + } + + // Fix up the "good channel list based on any errors reported. + if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1) + { + pB->i2eGoodMap[0] &= ~0x0f; + } + + if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2) + { + pB->i2eGoodMap[0] &= ~0xf0; + } + + break; // POR_ID_FII case + + case POR_ID_FIIEX: // IntelliPort-IIEX + + pB->i2eFifoStyle = FIFO_IIEX; + + itemp = pB->i2ePom.e.porFifoSize; + + // Implicit assumption that fifo would not grow beyond 32k, + // nor would ever be less than 256. + + if (itemp < 8 || itemp > 15) + { + COMPLETE(pB, I2EE_INCONSIST); + } + pB->i2eFifoSize = (1 << itemp); + + // These are based on what P.O.S.T thinks should be there, based on + // box ID registers + ilimit = pB->i2ePom.e.porNumBoxes; + if (ilimit > ABS_MAX_BOXES) + { + ilimit = ABS_MAX_BOXES; + } + + // For as many boxes as EXIST, gives the type of box. + // Added 8/6/93: check for the ISA-4 (asic) which looks like an + // expandable but for whom "8 or 16?" is not the right question. + + utemp = pB->i2ePom.e.porFlags; + if (utemp & POR_CEX4) + { + pB->i2eChannelMap[0] = 0x000f; + } else { + utemp &= POR_BOXES; + for (itemp = 0; itemp < ilimit; itemp++) + { + pB->i2eChannelMap[itemp] = + ((utemp & POR_BOX_16) ? 0xffff : 0x00ff); + utemp >>= 1; + } + } + + // These are based on what P.O.S.T actually found. + + utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1; + + for (itemp = 0; itemp < ilimit; itemp++) + { + pB->i2eGoodMap[itemp] = 0; + if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f; + if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0; + if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00; + if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000; + utemp >>= 4; + } + + // Now determine whether we should transfer in 8 or 16-bit mode. + switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) ) + { + case POR_BUS_SLOT16 | POR_BUS_DIP16: + pB->i2eDataWidth16 = YES; + pB->i2eMaxIrq = 15; + break; + + case POR_BUS_SLOT16: + pB->i2eDataWidth16 = NO; + pB->i2eMaxIrq = 15; + break; + + case 0: + case POR_BUS_DIP16: // In an 8-bit slot, DIP switch don't care. + default: + pB->i2eDataWidth16 = NO; + pB->i2eMaxIrq = 7; + break; + } + break; // POR_ID_FIIEX case + + default: // Unknown type of board + COMPLETE(pB, I2EE_BAD_FAMILY); + break; + } // End the switch based on family + + // Temporarily, claim there is no room in the outbound fifo. + // We will maintain this whenever we check for an empty outbound FIFO. + pB->i2eFifoRemains = 0; + + // Now, based on the bus type, should we expect to be able to re-configure + // interrupts (say, for testing purposes). + switch (pB->i2ePom.e.porBus & POR_BUS_TYPE) + { + case POR_BUS_T_ISA: + case POR_BUS_T_UNK: // If the type of bus is undeclared, assume ok. + pB->i2eChangeIrq = YES; + break; + case POR_BUS_T_MCA: + case POR_BUS_T_EISA: + pB->i2eChangeIrq = NO; + break; + default: + COMPLETE(pB, I2EE_BADBUS); + } + + if (pB->i2eDataWidth16 == YES) + { + pB->i2eWriteBuf = iiWriteBuf16; + pB->i2eReadBuf = iiReadBuf16; + pB->i2eWriteWord = iiWriteWord16; + pB->i2eReadWord = iiReadWord16; + } else { + pB->i2eWriteBuf = iiWriteBuf8; + pB->i2eReadBuf = iiReadBuf8; + pB->i2eWriteWord = iiWriteWord8; + pB->i2eReadWord = iiReadWord8; + } + + switch(pB->i2eFifoStyle) + { + case FIFO_II: + pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII; + pB->i2eTxMailEmpty = iiTxMailEmptyII; + pB->i2eTrySendMail = iiTrySendMailII; + pB->i2eGetMail = iiGetMailII; + pB->i2eEnableMailIrq = iiEnableMailIrqII; + pB->i2eWriteMask = iiWriteMaskII; + + break; + + case FIFO_IIEX: + pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX; + pB->i2eTxMailEmpty = iiTxMailEmptyIIEX; + pB->i2eTrySendMail = iiTrySendMailIIEX; + pB->i2eGetMail = iiGetMailIIEX; + pB->i2eEnableMailIrq = iiEnableMailIrqIIEX; + pB->i2eWriteMask = iiWriteMaskIIEX; + + break; + + default: + COMPLETE(pB, I2EE_INCONSIST); + } + + // Initialize state information. + pB->i2eState = II_STATE_READY; // Ready to load loadware. + + // Some Final cleanup: + // For some boards, the bootstrap firmware may perform some sort of test + // resulting in a stray character pending in the incoming mailbox. If one is + // there, it should be read and discarded, especially since for the standard + // firmware, it's the mailbox that interrupts the host. + + pB->i2eStartMail = iiGetMail(pB); + + // Everything is ok now, return with good status/ + + pB->i2eValid = I2E_MAGIC; + COMPLETE(pB, I2EE_GOOD); +} + +//======================================================= +// Delay Routines +// +// iiDelayIO +// iiNop +//======================================================= + +static void +ii2DelayWakeup(unsigned long id) +{ + wake_up_interruptible ( &pDelayWait ); +} + +//****************************************************************************** +// Function: ii2DelayTimer(mseconds) +// Parameters: mseconds - number of milliseconds to delay +// +// Returns: Nothing +// +// Description: +// +// This routine delays for approximately mseconds milliseconds and is intended +// to be called indirectly through i2Delay field in i2eBordStr. It uses the +// Linux timer_list mechanism. +// +// The Linux timers use a unit called "jiffies" which are 10mS in the Intel +// architecture. This function rounds the delay period up to the next "jiffy". +// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended +// for Alpha platforms at this time. +// +//****************************************************************************** +static void +ii2DelayTimer(unsigned int mseconds) +{ + init_timer ( pDelayTimer ); + + pDelayTimer->expires = jiffies + ( mseconds + 9 ) / 10; + pDelayTimer->function = ii2DelayWakeup; + pDelayTimer->data = 0; + + add_timer ( pDelayTimer ); + interruptible_sleep_on ( &pDelayWait ); + del_timer ( pDelayTimer ); +} + +#if 0 +//static void ii2DelayIO(unsigned int); +//****************************************************************************** +// !!! Not Used, this is DOS crap, some of you young folks may be interested in +// in how things were done in the stone age of caculating machines !!! +// Function: ii2DelayIO(mseconds) +// Parameters: mseconds - number of milliseconds to delay +// +// Returns: Nothing +// +// Description: +// +// This routine delays for approximately mseconds milliseconds and is intended +// to be called indirectly through i2Delay field in i2eBordStr. It is intended +// for use where a clock-based function is impossible: for example, DOS drivers. +// +// This function uses the IN instruction to place bounds on the timing and +// assumes that ii2Safe has been set. This is because I/O instructions are not +// subject to caching and will therefore take a certain minimum time. To ensure +// the delay is at least long enough on fast machines, it is based on some +// fastest-case calculations. On slower machines this may cause VERY long +// delays. (3 x fastest case). In the fastest case, everything is cached except +// the I/O instruction itself. +// +// Timing calculations: +// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O +// operation in question is a byte operation to an odd address. For 8-bit +// operations, the architecture generally enforces two wait states. At 10 MHz, a +// single cycle time is 100nS. A read operation at two wait states takes 6 +// cycles for a total time of 600nS. Therefore approximately 1666 iterations +// would be required to generate a single millisecond delay. The worst +// (reasonable) case would be an 8MHz system with no cacheing. In this case, the +// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code +// fetch of other instructions in the loop would take time (zero wait states, +// however) and would be hard to estimate. This is minimized by using in-line +// assembler for the in inner loop of IN instructions. This consists of just a +// few bytes. So we'll guess about four code fetches per loop. Each code fetch +// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is +// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS. +// +// So much for theoretical timings: results using 1666 value on some actual +// machines: +// IBM 286 6MHz 3.15 mS +// Zenith 386 33MHz 2.45 mS +// (brandX) 386 33MHz 1.90 mS (has cache) +// (brandY) 486 33MHz 2.35 mS +// NCR 486 ?? 1.65 mS (microchannel) +// +// For most machines, it is probably safe to scale this number back (remember, +// for robust operation use an actual timed delay if possible), so we are using +// a value of 1190. This yields 1.17 mS for the fastest machine in our sample, +// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine. +// +// 1/29/93: +// The above timings are too slow. Actual cycle times might be faster. ISA cycle +// times could approach 500 nS, and ... +// The IBM model 77 being microchannel has no wait states for 8-bit reads and +// seems to be accessing the I/O at 440 nS per access (from start of one to +// start of next). This would imply we need 1000/.440 = 2272 iterations to +// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in +// fact enough. For diagnostics, we keep the level at 1190, but developers note +// this needs tuning. +// +// Safe assumption: 2270 i/o reads = 1 millisecond +// +//****************************************************************************** + + +static int ii2DelValue = 1190; // See timing calculations below + // 1666 for fastest theoretical machine + // 1190 safe for most fast 386 machines + // 1000 for fastest machine tested here + // 540 (sic) for AT286/6Mhz +static void +ii2DelayIO(unsigned int mseconds) +{ + if (!ii2Safe) + return; /* Do nothing if this variable uninitialized */ + + while(mseconds--) { + int i = ii2DelValue; + while ( i-- ) { + INB ( ii2Safe ); + } + } +} +#endif + +//****************************************************************************** +// Function: ii2Nop() +// Parameters: None +// +// Returns: Nothing +// +// Description: +// +// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This +// saves checking for a NULL pointer at every call. +//****************************************************************************** +static void +ii2Nop(void) +{ + return; // no mystery here +} + +//======================================================= +// Routines which are available in 8/16-bit versions, or +// in different fifo styles. These are ALL called +// indirectly through the board structure. +//======================================================= + +//****************************************************************************** +// Function: iiWriteBuf16(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address of data to write +// count - number of data bytes to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes 'count' bytes from 'address' to the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// sent (identity unknown...). Uses 16-bit (word) operations. Is called +// indirectly through pB->i2eWriteBuf. +// +//****************************************************************************** +static int +iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + OUTSW ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiWriteBuf8(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address of data to write +// count - number of data bytes to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes 'count' bytes from 'address' to the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// sent (identity unknown...). This is to be consistant with the 16-bit version. +// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf. +// +//****************************************************************************** +static int +iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count) +{ + /* Rudimentary sanity checking here */ + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + OUTSB ( pB->i2eData, address, count ); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadBuf16(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address to put data read +// count - number of data bytes to read +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Reads 'count' bytes into 'address' from the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// received (identity unknown...). Uses 16-bit (word) operations. Is called +// indirectly through pB->i2eReadBuf. +// +//****************************************************************************** +static int +iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + INSW ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadBuf8(pB, address, count) +// Parameters: pB - pointer to board structure +// address - address to put data read +// count - number of data bytes to read +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Reads 'count' bytes into 'address' from the data fifo specified by the board +// structure pointer pB. Should count happen to be odd, an extra pad byte is +// received (identity unknown...). This to match the 16-bit behaviour. Uses +// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf. +// +//****************************************************************************** +static int +iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count) +{ + // Rudimentary sanity checking here. + if (pB->i2eValid != I2E_MAGIC) + COMPLETE(pB, I2EE_INVALID); + + INSB ( pB->i2eData, address, count); + + COMPLETE(pB, I2EE_GOOD); +} + +//****************************************************************************** +// Function: iiReadWord16(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Returns the word read from the data fifo specified by the board-structure +// pointer pB. Uses a 16-bit operation. Is called indirectly through +// pB->i2eReadWord. +// +//****************************************************************************** +static unsigned short +iiReadWord16(i2eBordStrPtr pB) +{ + return (unsigned short)( INW(pB->i2eData) ); +} + +//****************************************************************************** +// Function: iiReadWord8(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Returns the word read from the data fifo specified by the board-structure +// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is +// called indirectly through pB->i2eReadWord. +// +//****************************************************************************** +static unsigned short +iiReadWord8(i2eBordStrPtr pB) +{ + unsigned short urs; + + urs = INB ( pB->i2eData ); + + return ( ( INB ( pB->i2eData ) << 8 ) | urs ); +} + +//****************************************************************************** +// Function: iiWriteWord16(pB, value) +// Parameters: pB - pointer to board structure +// value - data to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes the word 'value' to the data fifo specified by the board-structure +// pointer pB. Uses 16-bit operation. Is called indirectly through +// pB->i2eWriteWord. +// +//****************************************************************************** +static void +iiWriteWord16(i2eBordStrPtr pB, unsigned short value) +{ + WORD_TO(pB, (int)value); +} + +//****************************************************************************** +// Function: iiWriteWord8(pB, value) +// Parameters: pB - pointer to board structure +// value - data to write +// +// Returns: True if everything appears copacetic. +// False if there is any error: the pB->i2eError field has the error +// +// Description: +// +// Writes the word 'value' to the data fifo specified by the board-structure +// pointer pB. Uses two 8-bit operations (writes LSB first). Is called +// indirectly through pB->i2eWriteWord. +// +//****************************************************************************** +static void +iiWriteWord8(i2eBordStrPtr pB, unsigned short value) +{ + BYTE_TO(pB, (char)value); + BYTE_TO(pB, (char)(value >> 8) ); +} + +//****************************************************************************** +// Function: iiWaitForTxEmptyII(pB, mSdelay) +// Parameters: pB - pointer to board structure +// mSdelay - period to wait before returning +// +// Returns: True if the FIFO is empty. +// False if it not empty in the required time: the pB->i2eError +// field has the error. +// +// Description: +// +// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if +// not empty by the required time, returns false and error in pB->i2eError, +// otherwise returns true. +// +// mSdelay == 0 is taken to mean must be empty on the first test. +// +// This version operates on IntelliPort-II - style FIFO's +// +// Note this routine is organized so that if status is ok there is no delay at +// all called either before or after the test. Is called indirectly through +// pB->i2eWaitForTxEmpty. +// +//****************************************************************************** +static int +iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay) +{ + unsigned long flags; + int itemp; + + for (;;) + { + // This routine hinges on being able to see the "other" status register + // (as seen by the local processor). His incoming fifo is our outgoing + // FIFO. + // + // By the nature of this routine, you would be using this as part of a + // larger atomic context: i.e., you would use this routine to ensure the + // fifo empty, then act on this information. Between these two halves, + // you will generally not want to service interrupts or in any way + // disrupt the assumptions implicit in the larger context. + // + // Even worse, however, this routine "shifts" the status register to + // point to the local status register which is not the usual situation. + // Therefore for extra safety, we force the critical section to be + // completely atomic, and pick up after ourselves before allowing any + // interrupts of any kind. + + + WRITE_LOCK_IRQSAVE(&Dl_spinlock,flags) + OUTB(pB->i2ePointer, SEL_COMMAND); + OUTB(pB->i2ePointer, SEL_CMD_SH); + + itemp = INB(pB->i2eStatus); + + OUTB(pB->i2ePointer, SEL_COMMAND); + OUTB(pB->i2ePointer, SEL_CMD_UNSH); + + if (itemp & ST_IN_EMPTY) + { + UPDATE_FIFO_ROOM(pB); + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + COMPLETE(pB, I2EE_GOOD); + } + + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + + if (mSdelay-- == 0) + break; + + iiDelay(pB, 1); /* 1 mS granularity on checking condition */ + } + COMPLETE(pB, I2EE_TXE_TIME); +} + +//****************************************************************************** +// Function: iiWaitForTxEmptyIIEX(pB, mSdelay) +// Parameters: pB - pointer to board structure +// mSdelay - period to wait before returning +// +// Returns: True if the FIFO is empty. +// False if it not empty in the required time: the pB->i2eError +// field has the error. +// +// Description: +// +// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if +// not empty by the required time, returns false and error in pB->i2eError, +// otherwise returns true. +// +// mSdelay == 0 is taken to mean must be empty on the first test. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +// Note this routine is organized so that if status is ok there is no delay at +// all called either before or after the test. Is called indirectly through +// pB->i2eWaitForTxEmpty. +// +//****************************************************************************** +static int +iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay) +{ + unsigned long flags; + + for (;;) + { + // By the nature of this routine, you would be using this as part of a + // larger atomic context: i.e., you would use this routine to ensure the + // fifo empty, then act on this information. Between these two halves, + // you will generally not want to service interrupts or in any way + // disrupt the assumptions implicit in the larger context. + + WRITE_LOCK_IRQSAVE(&Dl_spinlock,flags) + + if (INB(pB->i2eStatus) & STE_OUT_MT) { + UPDATE_FIFO_ROOM(pB); + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + COMPLETE(pB, I2EE_GOOD); + } + WRITE_UNLOCK_IRQRESTORE(&Dl_spinlock,flags) + + if (mSdelay-- == 0) + break; + + iiDelay(pB, 1); // 1 mS granularity on checking condition + } + COMPLETE(pB, I2EE_TXE_TIME); +} + +//****************************************************************************** +// Function: iiTxMailEmptyII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if the transmit mailbox is empty. +// False if it not empty. +// +// Description: +// +// Returns true or false according to whether the transmit mailbox is empty (and +// therefore able to accept more mail) +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static int +iiTxMailEmptyII(i2eBordStrPtr pB) +{ + int port = pB->i2ePointer; + OUTB ( port, SEL_OUTMAIL ); + return ( INB(port) == 0 ); +} + +//****************************************************************************** +// Function: iiTxMailEmptyIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: True if the transmit mailbox is empty. +// False if it not empty. +// +// Description: +// +// Returns true or false according to whether the transmit mailbox is empty (and +// therefore able to accept more mail) +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static int +iiTxMailEmptyIIEX(i2eBordStrPtr pB) +{ + return !(INB(pB->i2eStatus) & STE_OUT_MAIL); +} + +//****************************************************************************** +// Function: iiTrySendMailII(pB,mail) +// Parameters: pB - pointer to board structure +// mail - value to write to mailbox +// +// Returns: True if the transmit mailbox is empty, and mail is sent. +// False if it not empty. +// +// Description: +// +// If outgoing mailbox is empty, sends mail and returns true. If outgoing +// mailbox is not empty, returns false. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static int +iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail) +{ + int port = pB->i2ePointer; + + OUTB(port, SEL_OUTMAIL); + if (INB(port) == 0) { + OUTB(port, SEL_OUTMAIL); + OUTB(port, mail); + return 1; + } + return 0; +} + +//****************************************************************************** +// Function: iiTrySendMailIIEX(pB,mail) +// Parameters: pB - pointer to board structure +// mail - value to write to mailbox +// +// Returns: True if the transmit mailbox is empty, and mail is sent. +// False if it not empty. +// +// Description: +// +// If outgoing mailbox is empty, sends mail and returns true. If outgoing +// mailbox is not empty, returns false. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static int +iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail) +{ + if(INB(pB->i2eStatus) & STE_OUT_MAIL) { + return 0; + } + OUTB(pB->i2eXMail, mail); + return 1; +} + +//****************************************************************************** +// Function: iiGetMailII(pB,mail) +// Parameters: pB - pointer to board structure +// +// Returns: Mailbox data or NO_MAIL_HERE. +// +// Description: +// +// If no mail available, returns NO_MAIL_HERE otherwise returns the data from +// the mailbox, which is guaranteed != NO_MAIL_HERE. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static unsigned short +iiGetMailII(i2eBordStrPtr pB) +{ + if (HAS_MAIL(pB)) { + OUTB(pB->i2ePointer, SEL_INMAIL); + return INB(pB->i2ePointer); + } else { + return NO_MAIL_HERE; + } +} + +//****************************************************************************** +// Function: iiGetMailIIEX(pB,mail) +// Parameters: pB - pointer to board structure +// +// Returns: Mailbox data or NO_MAIL_HERE. +// +// Description: +// +// If no mail available, returns NO_MAIL_HERE otherwise returns the data from +// the mailbox, which is guaranteed != NO_MAIL_HERE. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static unsigned short +iiGetMailIIEX(i2eBordStrPtr pB) +{ + if (HAS_MAIL(pB)) { + return INB(pB->i2eXMail); + } else { + return NO_MAIL_HERE; + } +} + +//****************************************************************************** +// Function: iiEnableMailIrqII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Enables board to interrupt host (only) by writing to host's in-bound mailbox. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static void +iiEnableMailIrqII(i2eBordStrPtr pB) +{ + OUTB(pB->i2ePointer, SEL_MASK); + OUTB(pB->i2ePointer, ST_IN_MAIL); +} + +//****************************************************************************** +// Function: iiEnableMailIrqIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Enables board to interrupt host (only) by writing to host's in-bound mailbox. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static void +iiEnableMailIrqIIEX(i2eBordStrPtr pB) +{ + OUTB(pB->i2eXMask, MX_IN_MAIL); +} + +//****************************************************************************** +// Function: iiWriteMaskII(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Writes arbitrary value to the mask register. +// +// This version operates on IntelliPort-II - style FIFO's +// +//****************************************************************************** +static void +iiWriteMaskII(i2eBordStrPtr pB, unsigned char value) +{ + OUTB(pB->i2ePointer, SEL_MASK); + OUTB(pB->i2ePointer, value); +} + +//****************************************************************************** +// Function: iiWriteMaskIIEX(pB) +// Parameters: pB - pointer to board structure +// +// Returns: Nothing +// +// Description: +// +// Writes arbitrary value to the mask register. +// +// This version operates on IntelliPort-IIEX - style FIFO's +// +//****************************************************************************** +static void +iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value) +{ + OUTB(pB->i2eXMask, value); +} + +//****************************************************************************** +// Function: iiDownloadBlock(pB, pSource, isStandard) +// Parameters: pB - pointer to board structure +// pSource - loadware block to download +// isStandard - True if "standard" loadware, else false. +// +// Returns: Success or Failure +// +// Description: +// +// Downloads a single block (at pSource)to the board referenced by pB. Caller +// sets isStandard to true/false according to whether the "standard" loadware is +// what's being loaded. The normal process, then, is to perform an iiInitialize +// to the board, then perform some number of iiDownloadBlocks using the returned +// state to determine when download is complete. +// +// Possible return values: (see I2ELLIS.H) +// II_DOWN_BADVALID +// II_DOWN_BADFILE +// II_DOWN_CONTINUING +// II_DOWN_GOOD +// II_DOWN_BAD +// II_DOWN_BADSTATE +// II_DOWN_TIMEOUT +// +// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to +// determine whether this is the first block, whether to check for magic +// numbers, how many blocks there are to go... +// +//****************************************************************************** +static int +iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard) +{ + int itemp; + int loadedFirst; + + if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID; + + switch(pB->i2eState) + { + case II_STATE_READY: + + // Loading the first block after reset. Must check the magic number of the + // loadfile, store the number of blocks we expect to load. + if (pSource->e.loadMagic != MAGIC_LOADFILE) + { + return II_DOWN_BADFILE; + } + + // Next we store the total number of blocks to load, including this one. + pB->i2eToLoad = 1 + pSource->e.loadBlocksMore; + + // Set the state, store the version numbers. ('Cause this may have come + // from a file - we might want to report these versions and revisions in + // case of an error! + pB->i2eState = II_STATE_LOADING; + pB->i2eLVersion = pSource->e.loadVersion; + pB->i2eLRevision = pSource->e.loadRevision; + pB->i2eLSub = pSource->e.loadSubRevision; + + // The time and date of compilation is also available but don't bother + // storing it for normal purposes. + loadedFirst = 1; + break; + + case II_STATE_LOADING: + loadedFirst = 0; + break; + + default: + return II_DOWN_BADSTATE; + } + + // Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad + // must be positive still, because otherwise we would have cleaned up last + // time and set the state to II_STATE_LOADED. + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { + return II_DOWN_TIMEOUT; + } + + if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) { + return II_DOWN_BADVALID; + } + + // If we just loaded the first block, wait for the fifo to empty an extra + // long time to allow for any special startup code in the firmware, like + // sending status messages to the LCD's. + + if (loadedFirst) { + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) { + return II_DOWN_TIMEOUT; + } + } + + // Determine whether this was our last block! + if (--(pB->i2eToLoad)) { + return II_DOWN_CONTINUING; // more to come... + } + + // It WAS our last block: Clean up operations... + // ...Wait for last buffer to drain from the board... + if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) { + return II_DOWN_TIMEOUT; + } + // If there were only a single block written, this would come back + // immediately and be harmless, though not strictly necessary. + itemp = MAX_DLOAD_ACK_TIME/10; + while (--itemp) { + if (HAS_INPUT(pB)) { + switch(BYTE_FROM(pB)) + { + case LOADWARE_OK: + pB->i2eState = + isStandard ? II_STATE_STDLOADED :II_STATE_LOADED; + + // Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2) + // will, // if there is a debug port attached, require some + // time to send information to the debug port now. It will do + // this before // executing any of the code we just downloaded. + // It may take up to 700 milliseconds. + if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) { + iiDelay(pB, 700); + } + + return II_DOWN_GOOD; + + case LOADWARE_BAD: + default: + return II_DOWN_BAD; + } + } + + iiDelay(pB, 10); // 10 mS granularity on checking condition + } + + // Drop-through --> timed out waiting for firmware confirmation + + pB->i2eState = II_STATE_BADLOAD; + return II_DOWN_TIMEOUT; +} + +//****************************************************************************** +// Function: iiDownloadAll(pB, pSource, isStandard, size) +// Parameters: pB - pointer to board structure +// pSource - loadware block to download +// isStandard - True if "standard" loadware, else false. +// size - size of data to download (in bytes) +// +// Returns: Success or Failure +// +// Description: +// +// Given a pointer to a board structure, a pointer to the beginning of some +// loadware, whether it is considered the "standard loadware", and the size of +// the array in bytes loads the entire array to the board as loadware. +// +// Assumes the board has been freshly reset and the power-up reset message read. +// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be +// too much or too little data to load, or if iiDownloadBlock complains. +//****************************************************************************** +static int +iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size) +{ + int status; + + // We know (from context) board should be ready for the first block of + // download. Complain if not. + if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE; + + while (size > 0) { + size -= LOADWARE_BLOCK_SIZE; // How much data should there be left to + // load after the following operation ? + + // Note we just bump pSource by "one", because its size is actually that + // of an entire block, same as LOADWARE_BLOCK_SIZE. + status = iiDownloadBlock(pB, pSource++, isStandard); + + switch(status) + { + case II_DOWN_GOOD: + return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD); + + case II_DOWN_CONTINUING: + break; + + default: + return status; + } + } + + // We shouldn't drop out: it means "while" caught us with nothing left to + // download, yet the previous DownloadBlock did not return complete. Ergo, + // not enough data to match the size byte in the header. + return II_DOWN_UNDER; +} diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2ellis.h linux/drivers/char/ip2/i2ellis.h --- v2.2.11/linux/drivers/char/ip2/i2ellis.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2ellis.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,609 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// i2ellis.h +// +// IntelliPort-II and IntelliPort-IIEX +// +// Extremely +// Low +// Level +// Interface +// Services +// +// Structure Definitions and declarations for "ELLIS" service routines found in +// i2ellis.c +// +// These routines are based on properties of the IntelliPort-II and -IIEX +// hardware and bootstrap firmware, and are not sensitive to particular +// conventions of any particular loadware. +// +// Unlike i2hw.h, which provides IRONCLAD hardware definitions, the material +// here and in i2ellis.c is intended to provice a useful, but not required, +// layer of insulation from the hardware specifics. +//------------------------------------------------------------------------------ +#ifndef I2ELLIS_H /* To prevent multiple includes */ +#define I2ELLIS_H 1 +//------------------------------------------------ +// Revision History: +// +// 30 September 1991 MAG First Draft Started +// 12 October 1991 ...continued... +// +// 20 December 1996 AKM Linux version +//------------------------------------------------- + +//---------------------- +// Mandatory Includes: +//---------------------- +#include "ip2types.h" +#include "i2hw.h" // The hardware definitions + +//------------------------------------------ +// STAT_BOXIDS packets +//------------------------------------------ +#define MAX_BOX 4 + +typedef struct _bidStat +{ + unsigned char bid_value[MAX_BOX]; +} bidStat, *bidStatPtr; + +// This packet is sent in response to a CMD_GET_BOXIDS bypass command. For -IIEX +// boards, reports the hardware-specific "asynchronous resource register" on +// each expansion box. Boxes not present report 0xff. For -II boards, the first +// element contains 0x80 for 8-port, 0x40 for 4-port boards. + +// Box IDs aka ARR or Async Resource Register (more than you want to know) +// 7 6 5 4 3 2 1 0 +// F F N N L S S S +// ============================= +// F F - Product Family Designator +// =====+++++++++++++++++++++++++++++++ +// 0 0 - Intelliport II EX / ISA-8 +// 1 0 - IntelliServer +// 0 1 - SAC - Port Device (Intelliport III ??? ) +// =====+++++++++++++++++++++++++++++++++++++++ +// N N - Number of Ports +// 0 0 - 8 (eight) +// 0 1 - 4 (four) +// 1 0 - 12 (twelve) +// 1 1 - 16 (sixteen) +// =++++++++++++++++++++++++++++++++++ +// L - LCD Display Module Present +// 0 - No +// 1 - LCD module present +// =========+++++++++++++++++++++++++++++++++++++ +// S S S - Async Signals Supported Designator +// 0 0 0 - 8dss, Mod DCE DB25 Female +// 0 0 1 - 6dss, RJ-45 +// 0 1 0 - RS-232/422 dss, DB25 Female +// 0 1 1 - RS-232/422 dss, separate 232/422 DB25 Female +// 1 0 0 - 6dss, 921.6 I/F with ST654's +// 1 0 1 - RS-423/232 8dss, RJ-45 10Pin +// 1 1 0 - 6dss, Mod DCE DB25 Female +// 1 1 1 - NO BOX PRESENT + +#define FF(c) ((c & 0xC0) >> 6) +#define NN(c) ((c & 0x30) >> 4) +#define L(c) ((c & 0x08) >> 3) +#define SSS(c) (c & 0x07) + +#define BID_HAS_654(x) (SSS(x) == 0x04) +#define BID_NO_BOX 0xff /* no box */ +#define BID_8PORT 0x80 /* IP2-8 port */ +#define BID_4PORT 0x81 /* IP2-4 port */ +#define BID_EXP_MASK 0x30 /* IP2-EX */ +#define BID_EXP_8PORT 0x00 /* 8, */ +#define BID_EXP_4PORT 0x10 /* 4, */ +#define BID_EXP_UNDEF 0x20 /* UNDEF, */ +#define BID_EXP_16PORT 0x30 /* 16, */ +#define BID_LCD_CTRL 0x08 /* LCD Controller */ +#define BID_LCD_NONE 0x00 /* - no controller present */ +#define BID_LCD_PRES 0x08 /* - controller present */ +#define BID_CON_MASK 0x07 /* - connector pinouts */ +#define BID_CON_DB25 0x00 /* - DB-25 F */ +#define BID_CON_RJ45 0x01 /* - rj45 */ + +//------------------------------------------------------------------------------ +// i2eBordStr +// +// This structure contains all the information the ELLIS routines require in +// dealing with a particular board. +//------------------------------------------------------------------------------ +// There are some queues here which are guaranteed to never contain the entry +// for a single channel twice. So they must be slightly larger to allow +// unambiguous full/empty management +// +#define CH_QUEUE_SIZE ABS_MOST_PORTS+2 + +typedef struct _i2eBordStr +{ + porStr i2ePom; // Structure containing the power-on message. + + unsigned short i2ePomSize; + // The number of bytes actually read if + // different from sizeof i2ePom, indicates + // there is an error! + + unsigned short i2eStartMail; + // Contains whatever inbound mailbox data + // present at startup. NO_MAIL_HERE indicates + // nothing was present. No special + // significance as of this writing, but may be + // useful for diagnostic reasons. + + unsigned short i2eValid; + // Indicates validity of the structure; if + // i2eValid == I2E_MAGIC, then we can trust + // the other fields. Some (especially + // initialization) functions are good about + // checking for validity. Many functions do + // not, it being assumed that the larger + // context assures we are using a valid + // i2eBordStrPtr. + + unsigned short i2eError; + // Used for returning an error condition from + // several functions which use i2eBordStrPtr + // as an argument. + + // Accelerators to characterize separate features of a board, derived from a + // number of sources. + + unsigned short i2eFifoSize; + // Always, the size of the FIFO. For + // IntelliPort-II, always the same, for -IIEX + // taken from the Power-On reset message. + + volatile + unsigned short i2eFifoRemains; + // Used during normal operation to indicate a + // lower bound on the amount of data which + // might be in the outbound fifo. + + unsigned char i2eFifoStyle; + // Accelerator which tells which style (-II or + // -IIEX) FIFO we are using. + + unsigned char i2eDataWidth16; + // Accelerator which tells whether we should + // do 8 or 16-bit data transfers. + + unsigned char i2eMaxIrq; + // The highest allowable IRQ, based on the + // slot size. + + unsigned char i2eChangeIrq; + // Whether tis valid to change IRQ's + // ISA = ok, EISA, MicroChannel, no + + // Accelerators for various addresses on the board + int i2eBase; // I/O Address of the Board + int i2eData; // From here data transfers happen + int i2eStatus; // From here status reads happen + int i2ePointer; // (IntelliPort-II: pointer/commands) + int i2eXMail; // (IntelliPOrt-IIEX: mailboxes + int i2eXMask; // (IntelliPort-IIEX: mask write + + //------------------------------------------------------- + // Information presented in a common format across boards + // For each box, bit map of the channels present. Box closest to + // the host is box 0. LSB is channel 0. IntelliPort-II (non-expandable) + // is taken to be box 0. These are derived from product i.d. registers. + + unsigned short i2eChannelMap[ABS_MAX_BOXES]; + + // Same as above, except each is derived from firmware attempting to detect + // the uart presence (by reading a valid GFRCR register). If bits are set in + // i2eChannelMap and not in i2eGoodMap, there is a potential problem. + + unsigned short i2eGoodMap[ABS_MAX_BOXES]; + + // --------------------------- + // For indirect function calls + + // Routine to cause an N-millisecond delay: Patched by the ii2Initialize + // function. + + void (*i2eDelay)(unsigned int); + + // Routine to write N bytes to the board through the FIFO. Returns true if + // all copacetic, otherwise returns false and error is in i2eError field. + // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. + + int (*i2eWriteBuf)(struct _i2eBordStr *, unsigned char *, int); + + // Routine to read N bytes from the board through the FIFO. Returns true if + // copacetic, otherwise returns false and error in i2eError. + // IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER. + + int (*i2eReadBuf)(struct _i2eBordStr *, unsigned char *, int); + + // Returns a word from FIFO. Will use 2 byte operations if needed. + + unsigned short (*i2eReadWord)(struct _i2eBordStr *); + + // Writes a word to FIFO. Will use 2 byte operations if needed. + + void (*i2eWriteWord)(struct _i2eBordStr *, unsigned short); + + // Waits specified time for the Transmit FIFO to go empty. Returns true if + // ok, otherwise returns false and error in i2eError. + + int (*i2eWaitForTxEmpty)(struct _i2eBordStr *, int); + + // Returns true or false according to whether the outgoing mailbox is empty. + + int (*i2eTxMailEmpty)(struct _i2eBordStr *); + + // Checks whether outgoing mailbox is empty. If so, sends mail and returns + // true. Otherwise returns false. + + int (*i2eTrySendMail)(struct _i2eBordStr *, unsigned char); + + // If no mail available, returns NO_MAIL_HERE, else returns the value in the + // mailbox (guaranteed can't be NO_MAIL_HERE). + + unsigned short (*i2eGetMail)(struct _i2eBordStr *); + + // Enables the board to interrupt the host when it writes to the mailbox. + // Irqs will not occur, however, until the loadware separately enables + // interrupt generation to the host. The standard loadware does this in + // response to a command packet sent by the host. (Also, disables + // any other potential interrupt sources from the board -- other than the + // inbound mailbox). + + void (*i2eEnableMailIrq)(struct _i2eBordStr *); + + // Writes an arbitrary value to the mask register. + + void (*i2eWriteMask)(struct _i2eBordStr *, unsigned char); + + + // State information + + // During downloading, indicates the number of blocks remaining to download + // to the board. + + short i2eToLoad; + + // State of board (see manifests below) (e.g., whether in reset condition, + // whether standard loadware is installed, etc. + + unsigned char i2eState; + + // These three fields are only valid when there is loadware running on the + // board. (i2eState == II_STATE_LOADED or i2eState == II_STATE_STDLOADED ) + + unsigned char i2eLVersion; // Loadware version + unsigned char i2eLRevision; // Loadware revision + unsigned char i2eLSub; // Loadware subrevision + + // Flags which only have meaning in the context of the standard loadware. + // Somewhat violates the layering concept, but there is so little additional + // needed at the board level (while much additional at the channel level), + // that this beats maintaining two different per-board structures. + + // Indicates which IRQ the board has been initialized (from software) to use + // For MicroChannel boards, any value different from IRQ_UNDEFINED means + // that the software command has been sent to enable interrupts (or specify + // they are disabled). Special value: IRQ_UNDEFINED indicates that the + // software command to select the interrupt has not yet been sent, therefore + // (since the standard loadware insists that it be sent before any other + // packets are sent) no other packets should be sent yet. + + unsigned short i2eUsingIrq; + + // This is set when we hit the MB_OUT_STUFFED mailbox, which prevents us + // putting more in the mailbox until an appropriate mailbox message is + // received. + + unsigned char i2eWaitingForEmptyFifo; + + // Any mailbox bits waiting to be sent to the board are OR'ed in here. + + unsigned char i2eOutMailWaiting; + + // The head of any incoming packet is read into here, is then examined and + // we dispatch accordingly. + + unsigned short i2eLeadoffWord[1]; + + // Running counter of interrupts where the mailbox indicated incoming data. + + unsigned short i2eFifoInInts; + + // Running counter of interrupts where the mailbox indicated outgoing data + // had been stripped. + + unsigned short i2eFifoOutInts; + + // If not void, gives the address of a routine to call if fatal board error + // is found (only applies to standard l/w). + + void (*i2eFatalTrap)(struct _i2eBordStr *); + + // Will point to an array of some sort of channel structures (whose format + // is unknown at this level, being a function of what loadware is + // installed and the code configuration (max sizes of buffers, etc.)). + + void *i2eChannelPtr; + + // Set indicates that the board has gone fatal. + + unsigned short i2eFatal; + + // The number of elements pointed to by i2eChannelPtr. + + unsigned short i2eChannelCnt; + + // Ring-buffers of channel structures whose channels have particular needs. + + spinlock_t Fbuf_spinlock; + volatile + unsigned short i2Fbuf_strip; // Strip index + volatile + unsigned short i2Fbuf_stuff; // Stuff index + void *i2Fbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // flow control packets. + spinlock_t Dbuf_spinlock; + volatile + unsigned short i2Dbuf_strip; // Strip index + volatile + unsigned short i2Dbuf_stuff; // Stuff index + void *i2Dbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // data or in-line command packets. + spinlock_t Bbuf_spinlock; + volatile + unsigned short i2Bbuf_strip; // Strip index + volatile + unsigned short i2Bbuf_stuff; // Stuff index + void *i2Bbuf[CH_QUEUE_SIZE]; // An array of channel pointers + // of channels who need to send + // bypass command packets. + + /* + * A set of flags to indicate that certain events have occurred on at least + * one of the ports on this board. We use this to decide whether to spin + * through the channels looking for breaks, etc. + */ + int got_input; + int status_change; + bidStat channelBtypes; + + /* + * Debugging counters, etc. + */ + unsigned long debugFlowQueued; + unsigned long debugInlineQueued; + unsigned long debugDataQueued; + unsigned long debugBypassQueued; + unsigned long debugFlowCount; + unsigned long debugInlineCount; + unsigned long debugBypassCount; + + spinlock_t read_fifo_spinlock; + spinlock_t write_fifo_spinlock; + +} i2eBordStr, *i2eBordStrPtr; + +//------------------------------------------------------------------- +// Macro Definitions for the indirect calls defined in the i2eBordStr +//------------------------------------------------------------------- +// +#define iiDelay(a,b) (*(a)->i2eDelay)(b) +#define iiWriteBuf(a,b,c) (*(a)->i2eWriteBuf)(a,b,c) +#define iiReadBuf(a,b,c) (*(a)->i2eReadBuf)(a,b,c) + +#define iiWriteWord(a,b) (*(a)->i2eWriteWord)(a,b) +#define iiReadWord(a) (*(a)->i2eReadWord)(a) + +#define iiWaitForTxEmpty(a,b) (*(a)->i2eWaitForTxEmpty)(a,b) + +#define iiTxMailEmpty(a) (*(a)->i2eTxMailEmpty)(a) +#define iiTrySendMail(a,b) (*(a)->i2eTrySendMail)(a,b) + +#define iiGetMail(a) (*(a)->i2eGetMail)(a) +#define iiEnableMailIrq(a) (*(a)->i2eEnableMailIrq)(a) +#define iiDisableMailIrq(a) (*(a)->i2eWriteMask)(a,0) +#define iiWriteMask(a,b) (*(a)->i2eWriteMask)(a,b) + +//------------------------------------------- +// Manifests for i2eBordStr: +//------------------------------------------- + +#define YES 1 +#define NO 0 + +#define NULLFUNC (void (*)(void))0 +#define NULLPTR (void *)0 + +typedef void (*delayFunc_t)(unsigned int); + +// i2eValid +// +#define I2E_MAGIC 0x4251 // Structure is valid. +#define I2E_INCOMPLETE 0x1122 // Structure failed during init. + + +// i2eError +// +#define I2EE_GOOD 0 // Operation successful +#define I2EE_BADADDR 1 // Address out of range +#define I2EE_BADSTATE 2 // Attempt to perform a function when the board + // structure was in the incorrect state +#define I2EE_BADMAGIC 3 // Bad magic number from Power On test (i2ePomSize + // reflects what was read +#define I2EE_PORM_SHORT 4 // Power On message too short +#define I2EE_PORM_LONG 5 // Power On message too long +#define I2EE_BAD_FAMILY 6 // Un-supported board family type +#define I2EE_INCONSIST 7 // Firmware reports something impossible, + // e.g. unexpected number of ports... Almost no + // excuse other than bad FIFO... +#define I2EE_POSTERR 8 // Power-On self test reported a bad error +#define I2EE_BADBUS 9 // Unknown Bus type declared in message +#define I2EE_TXE_TIME 10 // Timed out waiting for TX Fifo to empty +#define I2EE_INVALID 11 // i2eValid field does not indicate a valid and + // complete board structure (for functions which + // require this be so.) +#define I2EE_BAD_PORT 12 // Discrepancy between channels actually found and + // what the product is supposed to have. Check + // i2eGoodMap vs i2eChannelMap for details. +#define I2EE_BAD_IRQ 13 // Someone specified an unsupported IRQ +#define I2EE_NOCHANNELS 14 // No channel structures have been defined (for + // functions requiring this). + +// i2eFifoStyle +// +#define FIFO_II 0 /* IntelliPort-II style: see also i2hw.h */ +#define FIFO_IIEX 1 /* IntelliPort-IIEX style */ + +// i2eGetMail +// +#define NO_MAIL_HERE 0x1111 // Since mail is unsigned char, cannot possibly + // promote to 0x1111. +// i2eState +// +#define II_STATE_COLD 0 // Addresses have been defined, but board not even + // reset yet. +#define II_STATE_RESET 1 // Board,if it exists, has just been reset +#define II_STATE_READY 2 // Board ready for its first block +#define II_STATE_LOADING 3 // Board continuing load +#define II_STATE_LOADED 4 // Board has finished load: status ok +#define II_STATE_BADLOAD 5 // Board has finished load: failed! +#define II_STATE_STDLOADED 6 // Board has finished load: standard firmware + +// i2eUsingIrq +// +#define IRQ_UNDEFINED 0x1352 // No valid irq (or polling = 0) can ever + // promote to this! +//------------------------------------------ +// Handy Macros for i2ellis.c and others +// Note these are common to -II and -IIEX +//------------------------------------------ + +// Given a pointer to the board structure, does the input FIFO have any data or +// not? +// +#define HAS_INPUT(pB) !(INB(pB->i2eStatus) & ST_IN_EMPTY) +#define HAS_NO_INPUT(pB) (INB(pB->i2eStatus) & ST_IN_EMPTY) + +// Given a pointer to board structure, read a byte or word from the fifo +// +#define BYTE_FROM(pB) (unsigned char)INB(pB->i2eData) +#define WORD_FROM(pB) (unsigned short)INW(pB->i2eData) + +// Given a pointer to board structure, is there room for any data to be written +// to the data fifo? +// +#define HAS_OUTROOM(pB) !(INB(pB->i2eStatus) & ST_OUT_FULL) +#define HAS_NO_OUTROOM(pB) (INB(pB->i2eStatus) & ST_OUT_FULL) + +// Given a pointer to board structure, write a single byte to the fifo +// structure. Note that for 16-bit interfaces, the high order byte is undefined +// and unknown. +// +#define BYTE_TO(pB, c) OUTB(pB->i2eData,(c)) + +// Write a word to the fifo structure. For 8-bit interfaces, this may have +// unknown results. +// +#define WORD_TO(pB, c) OUTW(pB->i2eData,(c)) + +// Given a pointer to the board structure, is there anything in the incoming +// mailbox? +// +#define HAS_MAIL(pB) (INB(pB->i2eStatus) & ST_IN_MAIL) + +#define UPDATE_FIFO_ROOM(pB) (pB)->i2eFifoRemains=(pB)->i2eFifoSize + +// Handy macro to round up a number (like the buffer write and read routines do) +// +#define ROUNDUP(number) (((number)+1) & (~1)) + +//------------------------------------------ +// Function Declarations for i2ellis.c +//------------------------------------------ +// +// Functions called directly +// +// Initialization of a board & structure is in four (five!) parts: +// +// 0) iiEllisInit() - Initialize iiEllis subsystem. +// 1) iiSetAddress() - Define the board address & delay function for a board. +// 2) iiReset() - Reset the board (provided it exists) +// -- Note you may do this to several boards -- +// 3) iiResetDelay() - Delay for 2 seconds (once for all boards) +// 4) iiInitialize() - Attempt to read Power-up message; further initialize +// accelerators +// +// Then you may use iiDownloadAll() or iiDownloadFile() (in i2file.c) to write +// loadware. To change loadware, you must begin again with step 2, resetting +// the board again (step 1 not needed). + +static void iiEllisInit(void); +static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t ); +static int iiReset(i2eBordStrPtr); +static int iiResetDelay(i2eBordStrPtr); +static int iiInitialize(i2eBordStrPtr); + +// Routine to validate that all channels expected are there. +// +extern int iiValidateChannels(i2eBordStrPtr); + +// Routine used to download a block of loadware. +// +static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int); + +// Return values given by iiDownloadBlock, iiDownloadAll, iiDownloadFile: +// +#define II_DOWN_BADVALID 0 // board structure is invalid +#define II_DOWN_CONTINUING 1 // So far, so good, firmware expects more +#define II_DOWN_GOOD 2 // Download complete, CRC good +#define II_DOWN_BAD 3 // Download complete, but CRC bad +#define II_DOWN_BADFILE 4 // Bad magic number in loadware file +#define II_DOWN_BADSTATE 5 // Board is in an inappropriate state for + // downloading loadware. (see i2eState) +#define II_DOWN_TIMEOUT 6 // Timeout waiting for firmware +#define II_DOWN_OVER 7 // Too much data +#define II_DOWN_UNDER 8 // Not enough data +#define II_DOWN_NOFILE 9 // Loadware file not found + +// Routine to download an entire loadware module: Return values are a subset of +// iiDownloadBlock's, excluding, of course, II_DOWN_CONTINUING +// +static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int); + +// Called indirectly always. Needed externally so the routine might be +// SPECIFIED as an argument to iiReset() +// +//static void ii2DelayIO(unsigned int); // N-millisecond delay using + //hardware spin +//static void ii2DelayTimer(unsigned int); // N-millisecond delay using Linux + //timer + +// Many functions defined here return True if good, False otherwise, with an +// error code in i2eError field. Here is a handy macro for setting the error +// code and returning. +// +#define COMPLETE(pB,code) \ + if(1){ \ + pB->i2eError = code; \ + return (code == I2EE_GOOD);\ + } + +#endif // I2ELLIS_H diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2hw.h linux/drivers/char/ip2/i2hw.h --- v2.2.11/linux/drivers/char/ip2/i2hw.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2hw.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,648 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions limited to properties of the hardware or the +* bootstrap firmware. As such, they are applicable regardless of +* operating system or loadware (standard or diagnostic). +* +*******************************************************************************/ +#ifndef I2HW_H +#define I2HW_H 1 +//------------------------------------------------------------------------------ +// Revision History: +// +// 23 September 1991 MAG First Draft Started...through... +// 11 October 1991 ... Continuing development... +// 6 August 1993 Added support for ISA-4 (asic) which is architected +// as an ISA-CEX with a single 4-port box. +// +// 20 December 1996 AKM Version for Linux +// +//------------------------------------------------------------------------------ +/*------------------------------------------------------------------------------ + +HARDWARE DESCRIPTION: + +Introduction: + +The IntelliPort-II and IntelliPort-IIEX products occupy a block of eight (8) +addresses in the host's I/O space. + +Some addresses are used to transfer data to/from the board, some to transfer +so-called "mailbox" messages, and some to read bit-mapped status information. +While all the products in the line are functionally similar, some use a 16-bit +data path to transfer data while others use an 8-bit path. Also, the use of +command /status/mailbox registers differs slightly between the II and IIEX +branches of the family. + +The host determines what type of board it is dealing with by reading a string of +sixteen characters from the board. These characters are always placed in the +fifo by the board's local processor whenever the board is reset (either from +power-on or under software control) and are known as the "Power-on Reset +Message." In order that this message can be read from either type of board, the +hardware registers used in reading this message are the same. Once this message +has been read by the host, then it has the information required to operate. + +General Differences between boards: + +The greatest structural difference is between the -II and -IIEX families of +product. The -II boards use the Am4701 dual 512x8 bidirectional fifo to support +the data path, mailbox registers, and status registers. This chip contains some +features which are not used in the IntelliPort-II products; a description of +these is omitted here. Because of these many features, it contains many +registers, too many to access directly within a small address space. They are +accessed by first writing a value to a "pointer" register. This value selects +the register to be accessed. The next read or write to that address accesses +the selected register rather than the pointer register. + +The -IIEX boards use a proprietary design similar to the Am4701 in function. But +because of a simpler, more streamlined design it doesn't require so many +registers. This means they can be accessed directly in single operations rather +than through a pointer register. + +Besides these differences, there are differences in whether 8-bit or 16-bit +transfers are used to move data to the board. + +The -II boards are capable only of 8-bit data transfers, while the -IIEX boards +may be configured for either 8-bit or 16-bit data transfers. If the on-board DIP +switch #8 is ON, and the card has been installed in a 16-bit slot, 16-bit +transfers are supported (and will be expected by the standard loadware). The +on-board firmware can determine the position of the switch, and whether the +board is installed in a 16-bit slot; it supplies this information to the host as +part of the power-up reset message. + +The configuration switch (#8) and slot selection do not directly configure the +hardware. It is up to the on-board loadware and host-based drivers to act +according to the selected options. That is, loadware and drivers could be +written to perform 8-bit transfers regardless of the state of the DIP switch or +slot (and in a diagnostic environment might well do so). Likewise, 16-bit +transfers could be performed as long as the card is in a 16-bit slot. + +Note the slot selection and DIP switch selection are provided separately: a +board running in 8-bit mode in a 16-bit slot has a greater range of possible +interrupts to choose from; information of potential use to the host. + +All 8-bit data transfers are done in the same way, regardless of whether on a +-II board or a -IIEX board. + +The host must consider two things then: 1) whether a -II or -IIEX product is +being used, and 2) whether an 8-bit or 16-bit data path is used. + +A further difference is that -II boards always have a 512-byte fifo operating in +each direction. -IIEX boards may use fifos of varying size; this size is +reported as part of the power-up message. + +I/O Map Of IntelliPort-II and IntelliPort-IIEX boards: +(Relative to the chosen base address) + +Addr R/W IntelliPort-II IntelliPort-IIEX +---- --- -------------- ---------------- +0 R/W Data Port (byte) Data Port (byte or word) +1 R/W (Not used) (MSB of word-wide data written to Data Port) +2 R Status Register Status Register +2 W Pointer Register Interrupt Mask Register +3 R/W (Not used) Mailbox Registers (6 bits: 11111100) +4,5 -- Reserved for future products +6 -- Reserved for future products +7 R Guaranteed to have no effect +7 W Hardware reset of board. + + +Rules: +All data transfers are performed using the even i/o address. If byte-wide data +transfers are being used, do INB/OUTB operations on the data port. If word-wide +transfers are used, do INW/OUTW operations. In some circumstances (such as +reading the power-up message) you will do INB from the data port, but in this +case the MSB of each word read is lost. When accessing all other unreserved +registers, use byte operations only. +------------------------------------------------------------------------------*/ + +//------------------------------------------------ +// Mandatory Includes: +//------------------------------------------------ +// +#include "ip2types.h" +#include "i2os.h" /* For any o.s., compiler, or host-related issues */ + +//------------------------------------------------------------------------- +// Manifests for the I/O map: +//------------------------------------------------------------------------- +// R/W: Data port (byte) for IntelliPort-II, +// R/W: Data port (byte or word) for IntelliPort-IIEX +// Incoming or outgoing data passes through a FIFO, the status of which is +// available in some of the bits in FIFO_STATUS. This (bidirectional) FIFO is +// the primary means of transferring data, commands, flow-control, and status +// information between the host and board. +// +#define FIFO_DATA 0 + +// Another way of passing information between the the board and the host is +// through "mailboxes". Unlike a FIFO, a mailbox holds only a single byte of +// data. Writing data to the mailbox causes a status bit to be set, and +// potentially interrupting the intended receiver. The sender has some way to +// determine whether the data has been read yet; as soon as it has, it may send +// more. The mailboxes are handled differently on -II and -IIEX products, as +// suggested below. +//------------------------------------------------------------------------------ +// Read: Status Register for IntelliPort-II or -IIEX +// The presence of any bit set here will cause an interrupt to the host, +// provided the corresponding bit has been unmasked in the interrupt mask +// register. Furthermore, interrupts to the host are disabled globally until the +// loadware selects the irq line to use. With the exception of STN_MR, the bits +// remain set so long as the associated condition is true. +// +#define FIFO_STATUS 2 + +// Bit map of status bits which are identical for -II and -IIEX +// +#define ST_OUT_FULL 0x40 // Outbound FIFO full +#define ST_IN_EMPTY 0x20 // Inbound FIFO empty +#define ST_IN_MAIL 0x04 // Inbound Mailbox full + +// The following exists only on the Intelliport-IIEX, and indicates that the +// board has not read the last outgoing mailbox data yet. In the IntelliPort-II, +// the outgoing mailbox may be read back: a zero indicates the board has read +// the data. +// +#define STE_OUT_MAIL 0x80 // Outbound mailbox full (!) + +// The following bits are defined differently for -II and -IIEX boards. Code +// which relies on these bits will need to be functionally different for the two +// types of boards and should be generally avoided because of the additional +// complexity this creates: + +// Bit map of status bits only on -II + +// Fifo has been RESET (cleared when the status register is read). Note that +// this condition cannot be masked and would always interrupt the host, except +// that the hardware reset also disables interrupts globally from the board +// until re-enabled by loadware. This could also arise from the +// Am4701-supported command to reset the chip, but this command is generally not +// used here. +// +#define STN_MR 0x80 + +// See the AMD Am4701 data sheet for details on the following four bits. They +// are not presently used by Computone drivers. +// +#define STN_OUT_AF 0x10 // Outbound FIFO almost full (programmable) +#define STN_IN_AE 0x08 // Inbound FIFO almost empty (programmable) +#define STN_BD 0x02 // Inbound byte detected +#define STN_PE 0x01 // Parity/Framing condition detected + +// Bit-map of status bits only on -IIEX +// +#define STE_OUT_HF 0x10 // Outbound FIFO half full +#define STE_IN_HF 0x08 // Inbound FIFO half full +#define STE_IN_FULL 0x02 // Inbound FIFO full +#define STE_OUT_MT 0x01 // Outbound FIFO empty + +//------------------------------------------------------------------------------ + +// Intelliport-II -- Write Only: the pointer register. +// Values are written to this register to select the Am4701 internal register to +// be accessed on the next operation. +// +#define FIFO_PTR 0x02 + +// Values for the pointer register +// +#define SEL_COMMAND 0x1 // Selects the Am4701 command register + +// Some possible commands: +// +#define SEL_CMD_MR 0x80 // Am4701 command to reset the chip +#define SEL_CMD_SH 0x40 // Am4701 command to map the "other" port into the + // status register. +#define SEL_CMD_UNSH 0 // Am4701 command to "unshift": port maps into its + // own status register. +#define SEL_MASK 0x2 // Selects the Am4701 interrupt mask register. The + // interrupt mask register is bit-mapped to match + // the status register (FIFO_STATUS) except for + // STN_MR. (See above.) +#define SEL_BYTE_DET 0x3 // Selects the Am4701 byte-detect register. (Not + // normally used except in diagnostics.) +#define SEL_OUTMAIL 0x4 // Selects the outbound mailbox (R/W). Reading back + // a value of zero indicates that the mailbox has + // been read by the board and is available for more + // data./ Writing to the mailbox optionally + // interrupts the board, depending on the loadware's + // setting of its interrupt mask register. +#define SEL_AEAF 0x5 // Selects AE/AF threshold register. +#define SEL_INMAIL 0x6 // Selects the inbound mailbox (Read) + +//------------------------------------------------------------------------------ +// IntelliPort-IIEX -- Write Only: interrupt mask (and misc flags) register: +// Unlike IntelliPort-II, bit assignments do NOT match those of the status +// register. +// +#define FIFO_MASK 0x2 + +// Mailbox readback select: +// If set, reads to FIFO_MAIL will read the OUTBOUND mailbox (host to board). If +// clear (default on reset) reads to FIFO_MAIL will read the INBOUND mailbox. +// This is the normal situation. The clearing of a mailbox is determined on +// -IIEX boards by waiting for the STE_OUT_MAIL bit to clear. Readback +// capability is provided for diagnostic purposes only. +// +#define MX_OUTMAIL_RSEL 0x80 + +#define MX_IN_MAIL 0x40 // Enables interrupts when incoming mailbox goes + // full (ST_IN_MAIL set). +#define MX_IN_FULL 0x20 // Enables interrupts when incoming FIFO goes full + // (STE_IN_FULL). +#define MX_IN_MT 0x08 // Enables interrupts when incoming FIFO goes empty + // (ST_IN_MT). +#define MX_OUT_FULL 0x04 // Enables interrupts when outgoing FIFO goes full + // (ST_OUT_FULL). +#define MX_OUT_MT 0x01 // Enables interrupts when outgoing FIFO goes empty + // (STE_OUT_MT). + +// Any remaining bits are reserved, and should be written to ZERO for +// compatibility with future Computone products. + +//------------------------------------------------------------------------------ +// IntelliPort-IIEX: -- These are only 6-bit mailboxes !!! -- 11111100 (low two +// bits always read back 0). +// Read: One of the mailboxes, usually Inbound. +// Inbound Mailbox (MX_OUTMAIL_RSEL = 0) +// Outbound Mailbox (MX_OUTMAIL_RSEL = 1) +// Write: Outbound Mailbox +// For the IntelliPort-II boards, the outbound mailbox is read back to determine +// whether the board has read the data (0 --> data has been read). For the +// IntelliPort-IIEX, this is done by reading a status register. To determine +// whether mailbox is available for more outbound data, use the STE_OUT_MAIL bit +// in FIFO_STATUS. Moreover, although the Outbound Mailbox can be read back by +// setting MX_OUTMAIL_RSEL, it is NOT cleared when the board reads it, as is the +// case with the -II boards. For this reason, FIFO_MAIL is normally used to read +// the inbound FIFO, and MX_OUTMAIL_RSEL kept clear. (See above for +// MX_OUTMAIL_RSEL description.) +// +#define FIFO_MAIL 0x3 + +//------------------------------------------------------------------------------ +// WRITE ONLY: Resets the board. (Data doesn't matter). +// +#define FIFO_RESET 0x7 + +//------------------------------------------------------------------------------ +// READ ONLY: Will have no effect. (Data is undefined.) +// Actually, there will be an effect, in that the operation is sure to generate +// a bus cycle: viz., an I/O byte Read. This fact can be used to enforce short +// delays when no comparable time constant is available. +// +#define FIFO_NOP 0x7 + +//------------------------------------------------------------------------------ +// RESET & POWER-ON RESET MESSAGE +/*------------------------------------------------------------------------------ +RESET: + +The IntelliPort-II and -IIEX boards are reset in three ways: Power-up, channel +reset, and via a write to the reset register described above. For products using +the ISA bus, these three sources of reset are equvalent. For MCA and EISA buses, +the Power-up and channel reset sources cause additional hardware initialization +which should only occur at system startup time. + +The third type of reset, called a "command reset", is done by writing any data +to the FIFO_RESET address described above. This resets the on-board processor, +FIFO, UARTS, and associated hardware. + +This passes control of the board to the bootstrap firmware, which performs a +Power-On Self Test and which detects its current configuration. For example, +-IIEX products determine the size of FIFO which has been installed, and the +number and type of expansion boxes attached. + +This and other information is then written to the FIFO in a 16-byte data block +to be read by the host. This block is guaranteed to be present within two (2) +seconds of having received the command reset. The firmware is now ready to +receive loadware from the host. + +It is good practice to perform a command reset to the board explicitly as part +of your software initialization. This allows your code to properly restart from +a soft boot. (Many systems do not issue channel reset on soft boot). + +Because of a hardware reset problem on some of the Cirrus Logic 1400's which are +used on the product, it is recommended that you reset the board twice, separated +by an approximately 50 milliseconds delay. (VERY approximately: probably ok to +be off by a factor of five. The important point is that the first command reset +in fact generates a reset pulse on the board. This pulse is guaranteed to last +less than 10 milliseconds. The additional delay ensures the 1400 has had the +chance to respond sufficiently to the first reset. Why not a longer delay? Much +more than 50 milliseconds gets to be noticable, but the board would still work. + +Once all 16 bytes of the Power-on Reset Message have been read, the bootstrap +firmware is ready to receive loadware. + +Note on Power-on Reset Message format: +The various fields have been designed with future expansion in view. +Combinations of bitfields and values have been defined which define products +which may not currently exist. This has been done to allow drivers to anticipate +the possible introduction of products in a systematic fashion. This is not +intended to suggest that each potential product is actually under consideration. +------------------------------------------------------------------------------*/ + +//---------------------------------------- +// Format of Power-on Reset Message +//---------------------------------------- + +typedef union _porStr // "por" stands for Power On Reset +{ + unsigned char c[16]; // array used when considering the message as a + // string of undifferentiated characters + + struct // Elements used when considering values + { + // The first two bytes out of the FIFO are two magic numbers. These are + // intended to establish that there is indeed a member of the + // IntelliPort-II(EX) family present. The remaining bytes may be + // expected // to be valid. When reading the Power-on Reset message, + // if the magic numbers do not match it is probably best to stop + // reading immediately. You are certainly not reading our board (unless + // hardware is faulty), and may in fact be reading some other piece of + // hardware. + + unsigned char porMagic1; // magic number: first byte == POR_MAGIC_1 + unsigned char porMagic2; // magic number: second byte == POR_MAGIC_2 + + // The Version, Revision, and Subrevision are stored as absolute numbers + // and would normally be displayed in the format V.R.S (e.g. 1.0.2) + + unsigned char porVersion; // Bootstrap firmware version number + unsigned char porRevision; // Bootstrap firmware revision number + unsigned char porSubRev; // Bootstrap firmware sub-revision number + + unsigned char porID; // Product ID: Bit-mapped according to + // conventions described below. Among other + // things, this allows us to distinguish + // IntelliPort-II boards from IntelliPort-IIEX + // boards. + + unsigned char porBus; // IntelliPort-II: Unused + // IntelliPort-IIEX: Bus Information: + // Bit-mapped below + + unsigned char porMemory; // On-board DRAM size: in 32k blocks + + // porPorts1 (and porPorts2) are used to determine the ports which are + // available to the board. For non-expandable product, a single number + // is sufficient. For expandable product, the board may be connected + // to as many as four boxes. Each box may be (so far) either a 16-port + // or an 8-port size. Whenever an 8-port box is used, the remaining 8 + // ports leave gaps between existing channels. For that reason, + // expandable products must report a MAP of available channels. Since + // each UART supports four ports, we represent each UART found by a + // single bit. Using two bytes to supply the mapping information we + // report the presense or absense of up to 16 UARTS, or 64 ports in + // steps of 4 ports. For -IIEX products, the ports are numbered + // starting at the box closest to the controller in the "chain". + + // Interpreted Differently for IntelliPort-II and -IIEX. + // -II: Number of ports (Derived actually from product ID). See + // Diag1&2 to indicate if uart was actually detected. + // -IIEX: Bit-map of UARTS found, LSB (see below for MSB of this). This + // bitmap is based on detecting the uarts themselves; + // see porFlags for information from the box i.d's. + unsigned char porPorts1; + + unsigned char porDiag1; // Results of on-board P.O.S.T, 1st byte + unsigned char porDiag2; // Results of on-board P.O.S.T, 2nd byte + unsigned char porSpeed; // Speed of local CPU: given as MHz x10 + // e.g., 16.0 MHz CPU is reported as 160 + unsigned char porFlags; // Misc information (see manifests below) + // Bit-mapped: CPU type, UART's present + + unsigned char porPorts2; // -II: Undefined + // -IIEX: Bit-map of UARTS found, MSB (see + // above for LSB) + + // IntelliPort-II: undefined + // IntelliPort-IIEX: 1 << porFifoSize gives the size, in bytes, of the + // host interface FIFO, in each direction. When running the -IIEX in + // 8-bit mode, fifo capacity is halved. The bootstrap firmware will + // have already accounted for this fact in generating this number. + unsigned char porFifoSize; + + // IntelliPort-II: undefined + // IntelliPort-IIEX: The number of boxes connected. (Presently 1-4) + unsigned char porNumBoxes; + } e; +} porStr, *porStrPtr; + +//-------------------------- +// Values for porStr fields +//-------------------------- + +//--------------------- +// porMagic1, porMagic2 +//---------------------- +// +#define POR_MAGIC_1 0x96 // The only valid value for porMagic1 +#define POR_MAGIC_2 0x35 // The only valid value for porMagic2 +#define POR_1_INDEX 0 // Byte position of POR_MAGIC_1 +#define POR_2_INDEX 1 // Ditto for POR_MAGIC_2 + +//---------------------- +// porID +//---------------------- +// +#define POR_ID_FAMILY 0xc0 // These bits indicate the general family of + // product. +#define POR_ID_FII 0x00 // Family is "IntelliPort-II" +#define POR_ID_FIIEX 0x40 // Family is "IntelliPort-IIEX" + +// These bits are reserved, presently zero. May be used at a later date to +// convey other product information. +// +#define POR_ID_RESERVED 0x3c + +#define POR_ID_SIZE 0x03 // Remaining bits indicate number of ports & + // Connector information. +#define POR_ID_II_8 0x00 // For IntelliPort-II, indicates 8-port using + // standard brick. +#define POR_ID_II_8R 0x01 // For IntelliPort-II, indicates 8-port using + // RJ11's (no CTS) +#define POR_ID_II_6 0x02 // For IntelliPort-II, indicates 6-port using + // RJ45's +#define POR_ID_II_4 0x03 // For IntelliPort-II, indicates 4-port using + // 4xRJ45 connectors +#define POR_ID_EX 0x00 // For IntelliPort-IIEX, indicates standard + // expandable controller (other values reserved) + +//---------------------- +// porBus +//---------------------- + +// IntelliPort-IIEX only: Board is installed in a 16-bit slot +// +#define POR_BUS_SLOT16 0x20 + +// IntelliPort-IIEX only: DIP switch #8 is on, selecting 16-bit host interface +// operation. +// +#define POR_BUS_DIP16 0x10 + +// Bits 0-2 indicate type of bus: This information is stored in the bootstrap +// loadware, different loadware being used on different products for different +// buses. For most situations, the drivers do not need this information; but it +// is handy in a diagnostic environment. For example, on microchannel boards, +// you would not want to try to test several interrupts, only the one for which +// you were configured. +// +#define POR_BUS_TYPE 0x07 + +// Unknown: this product doesn't know what bus it is running in. (e.g. if same +// bootstrap firmware were wanted for two different buses.) +// +#define POR_BUS_T_UNK 0 + +// Note: existing firmware for ISA-8 and MC-8 currently report the POR_BUS_T_UNK +// state, since the same bootstrap firmware is used for each. + +#define POR_BUS_T_MCA 1 // MCA BUS */ +#define POR_BUS_T_EISA 2 // EISA BUS */ +#define POR_BUS_T_ISA 3 // ISA BUS */ + +// Values 4-7 Reserved + +// Remaining bits are reserved + +//---------------------- +// porDiag1 +//---------------------- + +#define POR_BAD_MAPPER 0x80 // HW failure on P.O.S.T: Chip mapper failed + +// These two bits valid only for the IntelliPort-II +// +#define POR_BAD_UART1 0x01 // First 1400 bad +#define POR_BAD_UART2 0x02 // Second 1400 bad + +//---------------------- +// porDiag2 +//---------------------- + +#define POR_DEBUG_PORT 0x80 // debug port was detected by the P.O.S.T +#define POR_DIAG_OK 0x00 // Indicates passage: Failure codes not yet + // available. + // Other bits undefined. +//---------------------- +// porFlags +//---------------------- + +#define POR_CPU 0x03 // These bits indicate supposed CPU type +#define POR_CPU_8 0x01 // Board uses an 80188 (no such thing yet) +#define POR_CPU_6 0x02 // Board uses an 80186 (all existing products) +#define POR_CEX4 0x04 // If set, this is an ISA-CEX/4: An ISA-4 (asic) + // which is architected like an ISA-CEX connected + // to a (hitherto impossible) 4-port box. +#define POR_BOXES 0xf0 // Valid for IntelliPort-IIEX only: Map of Box + // sizes based on box I.D. +#define POR_BOX_16 0x10 // Set indicates 16-port, clear 8-port + +//------------------------------------- +// LOADWARE and DOWNLOADING CODE +//------------------------------------- + +/* +Loadware may be sent to the board in two ways: +1) It may be read from a (binary image) data file block by block as each block + is sent to the board. This is only possible when the initialization is + performed by code which can access your file system. This is most suitable + for diagnostics and appications which use the interface library directly. + +2) It may be hard-coded into your source by including a .h file (typically + supplied by Computone), which declares a data array and initializes every + element. This acheives the same result as if an entire loadware file had + been read into the array. + + This requires more data space in your program, but access to the file system + is not required. This method is more suited to driver code, which typically + is running at a level too low to access the file system directly. + +At present, loadware can only be generated at Computone. + +All Loadware begins with a header area which has a particular format. This +includes a magic number which identifies the file as being (purportedly) +loadware, CRC (for the loader), and version information. +*/ + + +//----------------------------------------------------------------------------- +// Format of loadware block +// +// This is defined as a union so we can pass a pointer to one of these items +// and (if it is the first block) pick out the version information, etc. +// +// Otherwise, to deal with this as a simple character array +//------------------------------------------------------------------------------ + +#define LOADWARE_BLOCK_SIZE 512 // Number of bytes in each block of loadware + +typedef union _loadHdrStr +{ + unsigned char c[LOADWARE_BLOCK_SIZE]; // Valid for every block + + struct // These fields are valid for only the first block of loadware. + { + unsigned char loadMagic; // Magic number: see below + unsigned char loadBlocksMore; // How many more blocks? + unsigned char loadCRC[2]; // Two CRC bytes: used by loader + unsigned char loadVersion; // Version number + unsigned char loadRevision; // Revision number + unsigned char loadSubRevision; // Sub-revision number + unsigned char loadSpares[9]; // Presently unused + unsigned char loadDates[32]; // Null-terminated string which can give + // date and time of compilation + } e; +} loadHdrStr, *loadHdrStrPtr; + +//------------------------------------ +// Defines for downloading code: +//------------------------------------ + +// The loadMagic field in the first block of the loadfile must be this, else the +// file is not valid. +// +#define MAGIC_LOADFILE 0x3c + +// How do we know the load was successful? On completion of the load, the +// bootstrap firmware returns a code to indicate whether it thought the download +// was valid and intends to execute it. These are the only possible valid codes: +// +#define LOADWARE_OK 0xc3 // Download was ok +#define LOADWARE_BAD 0x5a // Download was bad (CRC error) + +// Constants applicable to writing blocks of loadware: +// The first block of loadware might take 600 mS to load, in extreme cases. +// (Expandable board: worst case for sending startup messages to the LCD's). +// The 600mS figure is not really a calculation, but a conservative +// guess/guarantee. Usually this will be within 100 mS, like subsequent blocks. +// +#define MAX_DLOAD_START_TIME 1000 // 1000 mS +#define MAX_DLOAD_READ_TIME 100 // 100 mS + +// Firmware should respond with status (see above) within this long of host +// having sent the final block. +// +#define MAX_DLOAD_ACK_TIME 100 // 100 mS, again! + +//------------------------------------------------------ +// MAXIMUM NUMBER OF PORTS PER BOARD: +// This is fixed for now (with the expandable), but may +// be expanding according to even newer products. +//------------------------------------------------------ +// +#define ABS_MAX_BOXES 4 // Absolute most boxes per board +#define ABS_BIGGEST_BOX 16 // Absolute the most ports per box +#define ABS_MOST_PORTS (ABS_MAX_BOXES * ABS_BIGGEST_BOX) + +#endif // I2HW_H + diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2lib.c linux/drivers/char/ip2/i2lib.c --- v2.2.11/linux/drivers/char/ip2/i2lib.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2lib.c Wed Aug 25 17:29:47 1999 @@ -0,0 +1,2255 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: High-level interface code for the device driver. Uses the +* Extremely Low Level Interface Support (i2ellis.c). Provides an +* interface to the standard loadware, to support drivers or +* application code. (This is included source code, not a separate +* compilation module.) +* +*******************************************************************************/ +//------------------------------------------------------------------------------ +// Note on Strategy: +// Once the board has been initialized, it will interrupt us when: +// 1) It has something in the fifo for us to read (incoming data, flow control +// packets, or whatever). +// 2) It has stripped whatever we have sent last time in the FIFO (and +// consequently is ready for more). +// +// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This +// worsens performance considerably, but is done so that a great many channels +// might use only a little memory. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Revision History: +// +// 0.00 - 4/16/91 --- First Draft +// 0.01 - 4/29/91 --- 1st beta release +// 0.02 - 6/14/91 --- Changes to allow small model compilation +// 0.03 - 6/17/91 MAG Break reporting protected from interrupts routines with +// in-line asm added for moving data to/from ring buffers, +// replacing a variety of methods used previously. +// 0.04 - 6/21/91 MAG Initial flow-control packets not queued until +// i2_enable_interrupts time. Former versions would enqueue +// them at i2_init_channel time, before we knew how many +// channels were supposed to exist! +// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now; +// supports new 16-bit protocol and expandable boards. +// - 10/24/91 MAG Most changes in place and stable. +// 0.06 - 2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no +// argument. +// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt +// level (mostly responses to specific commands.) +// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet +// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE +// turning on the interrupt. +// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check +// some incoming. +// +// 1.1 - 12/25/96 AKM Linux version. +// - 10/09/98 DMC Revised Linux version. +//------------------------------------------------------------------------------ + +//************ +//* Includes * +//************ + +#include +#include "i2lib.h" + + +//*********************** +//* Function Prototypes * +//*********************** +static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int); +static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int ); +static void i2StripFifo(i2eBordStrPtr); +static void i2StuffFifoBypass(i2eBordStrPtr); +static void i2StuffFifoFlow(i2eBordStrPtr); +static void i2StuffFifoInline(i2eBordStrPtr); +static int i2RetryFlushOutput(i2ChanStrPtr); + +// Not a documented part of the library routines (careful...) but the Diagnostic +// i2diag.c finds them useful to help the throughput in certain limited +// single-threaded operations. +static void iiSendPendingMail(i2eBordStrPtr); +static void serviceOutgoingFifo(i2eBordStrPtr); + +// Functions defined in ip2.c as part of interrupt handling +static void do_input(i2ChanStrPtr); +static void do_status(i2ChanStrPtr); + +//*************** +//* Debug Data * +//*************** +#ifdef DEBUG_FIFO + +unsigned char DBGBuf[0x4000]; +unsigned short I = 0; + +static void +WriteDBGBuf(char *s, unsigned char *src, unsigned short n ) +{ + char *p = src; + + // XXX: We need a spin lock here if we ever use this again + + while (*s) { // copy label + DBGBuf[I] = *s++; + I = I++ & 0x3fff; + } + while (n--) { // copy data + DBGBuf[I] = *p++; + I = I++ & 0x3fff; + } +} + +static void +fatality(i2eBordStrPtr pB ) +{ + int i; + + for (i=0;i= ' ' && DBGBuf[i] <= '~') { + printk(" %c ",DBGBuf[i]); + } else { + printk(" . "); + } + } + printk("\n"); + printk("Last index %x\n",I); +} +#endif /* DEBUG_FIFO */ + +//******** +//* Code * +//******** + +inline int +i2Validate ( i2ChanStrPtr pCh ) +{ + //ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity, + // (CHANNEL_MAGIC | CHANNEL_SUPPORT)); + return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT)) + == (CHANNEL_MAGIC | CHANNEL_SUPPORT)); +} + +//****************************************************************************** +// Function: iiSendPendingMail(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// If any outgoing mail bits are set and there is outgoing mailbox is empty, +// send the mail and clear the bits. +//****************************************************************************** +static inline void +iiSendPendingMail(i2eBordStrPtr pB) +{ + if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) ) + { + if (iiTrySendMail(pB, pB->i2eOutMailWaiting)) + { + /* If we were already waiting for fifo to empty, + * or just sent MB_OUT_STUFFED, then we are + * still waiting for it to empty, until we should + * receive an MB_IN_STRIPPED from the board. + */ + pB->i2eWaitingForEmptyFifo |= + (pB->i2eOutMailWaiting & MB_OUT_STUFFED); + pB->i2eOutMailWaiting = 0; + } + } +} + +//****************************************************************************** +// Function: i2InitChannels(pB, nChannels, pCh) +// Parameters: Pointer to Ellis Board structure +// Number of channels to initialize +// Pointer to first element in an array of channel structures +// Returns: Success or failure +// +// Description: +// +// This function patches pointers, back-pointers, and initializes all the +// elements in the channel structure array. +// +// This should be run after the board structure is initialized, through having +// loaded the standard loadware (otherwise it complains). +// +// In any case, it must be done before any serious work begins initializing the +// irq's or sending commands... +// +//****************************************************************************** +static int +i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh) +{ + int index, stuffIndex; + i2ChanStrPtr *ppCh; + + if (pB->i2eValid != I2E_MAGIC) { + COMPLETE(pB, I2EE_BADMAGIC); + } + if (pB->i2eState != II_STATE_STDLOADED) { + COMPLETE(pB, I2EE_BADSTATE); + } + + LOCK_INIT(&pB->read_fifo_spinlock); + LOCK_INIT(&pB->write_fifo_spinlock); + LOCK_INIT(&pB->Dbuf_spinlock); + LOCK_INIT(&pB->Bbuf_spinlock); + LOCK_INIT(&pB->Fbuf_spinlock); + + // NO LOCK needed yet - this is init + + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nChannels; + + pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0; + pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0; + pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0; + + memset ( pCh, 0, sizeof (i2ChanStr) * nChannels ); + + for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf); + nChannels && index < ABS_MOST_PORTS; + index++) + { + if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) { + continue; + } + LOCK_INIT(&pCh->Ibuf_spinlock); + LOCK_INIT(&pCh->Obuf_spinlock); + LOCK_INIT(&pCh->Cbuf_spinlock); + LOCK_INIT(&pCh->Pbuf_spinlock); + // NO LOCK needed yet - this is init + // Set up validity flag according to support level + if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) { + pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT; + } else { + pCh->validity = CHANNEL_MAGIC; + } + pCh->pMyBord = pB; /* Back-pointer */ + + // Prepare an outgoing flow-control packet to send as soon as the chance + // occurs. + if ( pCh->validity & CHANNEL_SUPPORT ) { + pCh->infl.hd.i2sChannel = index; + pCh->infl.hd.i2sCount = 5; + pCh->infl.hd.i2sType = PTYPE_BYPASS; + pCh->infl.fcmd = 37; + pCh->infl.asof = 0; + pCh->infl.room = IBUF_SIZE - 1; + + pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full + + // The following is similar to calling i2QueueNeeds, except that this + // is done in longhand, since we are setting up initial conditions on + // many channels at once. + pCh->channelNeeds = NEED_FLOW; // Since starting from scratch + pCh->sinceLastFlow = 0; // No bytes received since last flow + // control packet was queued + stuffIndex++; + *ppCh++ = pCh; // List this channel as needing + // initial flow control packet sent + } + + // Don't allow anything to be sent until the status packets come in from + // the board. + + pCh->outfl.asof = 0; + pCh->outfl.room = 0; + + // Initialize all the ring buffers + + pCh->Ibuf_stuff = pCh->Ibuf_strip = 0; + pCh->Obuf_stuff = pCh->Obuf_strip = 0; + pCh->Cbuf_stuff = pCh->Cbuf_strip = 0; + + memset( &pCh->icount, 0, sizeof (struct async_icount) ); + pCh->hotKeyIn = HOT_CLEAR; + pCh->channelOptions = 0; + pCh->bookMarks = 0; + pCh->pBookmarkWait = NULL; + + pCh->open_wait = NULL; + pCh->close_wait = NULL; + pCh->delta_msr_wait = NULL; + + // Set base and divisor so default custom rate is 9600 + pCh->BaudBase = 921600; // MAX for ST654, changed after we get + pCh->BaudDivisor = 96; // the boxids (UART types) later + + pCh->dataSetIn = 0; + pCh->dataSetOut = 0; + + pCh->wopen = 0; + pCh->throttled = 0; + + pCh->speed = CBR_9600; + + pCh->flags = 0; + pCh->session = 0; + pCh->pgrp = 0; + + pCh->ClosingDelay = 5*HZ/10; + pCh->ClosingWaitTime = 30*HZ; + +#ifdef USE_IQ + // Initialize task queue objects + pCh->tqueue_input.routine = (void(*)(void*)) do_input; + pCh->tqueue_input.data = pCh; + pCh->tqueue_status.routine = (void(*)(void*)) do_status; + pCh->tqueue_status.data = pCh; +#endif + + pCh->trace = ip2trace; + + ++pCh; + --nChannels; + } + // No need to check for wrap here; this is initialization. + pB->i2Fbuf_stuff = stuffIndex; + COMPLETE(pB, I2EE_GOOD); + +} + +//****************************************************************************** +// Function: i2DeQueueNeeds(pB, type) +// Parameters: Pointer to a board structure +// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW +// Returns: +// Pointer to a channel structure +// +// Description: Returns pointer struct of next channel that needs service of +// the type specified. Otherwise returns a NULL reference. +// +//****************************************************************************** +static i2ChanStrPtr +i2DeQueueNeeds(i2eBordStrPtr pB, int type) +{ + unsigned short queueIndex; + unsigned long flags; + + i2ChanStrPtr pCh = NULL; + + switch(type) { + + case NEED_INLINE: + + WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); + if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip) + { + queueIndex = pB->i2Dbuf_strip; + pCh = pB->i2Dbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Dbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_INLINE; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); + break; + + case NEED_BYPASS: + + WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); + if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip) + { + queueIndex = pB->i2Bbuf_strip; + pCh = pB->i2Bbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Bbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_BYPASS; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); + break; + + case NEED_FLOW: + + WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); + if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip) + { + queueIndex = pB->i2Fbuf_strip; + pCh = pB->i2Fbuf[queueIndex]; + queueIndex++; + if (queueIndex >= CH_QUEUE_SIZE) { + queueIndex = 0; + } + pB->i2Fbuf_strip = queueIndex; + pCh->channelNeeds &= ~NEED_FLOW; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); + break; + default: + printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type); + break; + } + return pCh; +} + +//****************************************************************************** +// Function: i2QueueNeeds(pB, pCh, type) +// Parameters: Pointer to a board structure +// Pointer to a channel structure +// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW +// Returns: Nothing +// +// Description: +// For each type of need selected, if the given channel is not already in the +// queue, adds it, and sets the flag indicating it is in the queue. +//****************************************************************************** +static void +i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type) +{ + unsigned short queueIndex; + unsigned long flags; + + // We turn off all the interrupts during this brief process, since the + // interrupt-level code might want to put things on the queue as well. + + switch (type) { + + case NEED_INLINE: + + WRITE_LOCK_IRQSAVE(&pB->Dbuf_spinlock,flags); + if ( !(pCh->channelNeeds & NEED_INLINE) ) + { + pCh->channelNeeds |= NEED_INLINE; + queueIndex = pB->i2Dbuf_stuff; + pB->i2Dbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Dbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Dbuf_spinlock,flags); + break; + + case NEED_BYPASS: + + WRITE_LOCK_IRQSAVE(&pB->Bbuf_spinlock,flags); + if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS)) + { + pCh->channelNeeds |= NEED_BYPASS; + queueIndex = pB->i2Bbuf_stuff; + pB->i2Bbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Bbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Bbuf_spinlock,flags); + break; + + case NEED_FLOW: + + WRITE_LOCK_IRQSAVE(&pB->Fbuf_spinlock,flags); + if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW)) + { + pCh->channelNeeds |= NEED_FLOW; + queueIndex = pB->i2Fbuf_stuff; + pB->i2Fbuf[queueIndex++] = pCh; + if (queueIndex >= CH_QUEUE_SIZE) + queueIndex = 0; + pB->i2Fbuf_stuff = queueIndex; + } + WRITE_UNLOCK_IRQRESTORE(&pB->Fbuf_spinlock,flags); + break; + + case NEED_CREDIT: + pCh->channelNeeds |= NEED_CREDIT; + break; + default: + printk(KERN_ERR "i2QueueNeeds called with bad type:%x\n",type); + break; + } + return; +} + +//****************************************************************************** +// Function: i2QueueCommands(type, pCh, timeout, nCommands, pCs,...) +// Parameters: type - PTYPE_BYPASS or PTYPE_INLINE +// pointer to the channel structure +// maximum period to wait +// number of commands (n) +// n commands +// Returns: Number of commands sent, or -1 for error +// +// get board lock before calling +// +// Description: +// Queues up some commands to be sent to a channel. To send possibly several +// bypass or inline commands to the given channel. The timeout parameter +// indicates how many HUNDREDTHS OF SECONDS to wait until there is room: +// 0 = return immediately if no room, -ive = wait forever, +ive = number of +// 1/100 seconds to wait. Return values: +// -1 Some kind of nasty error: bad channel structure or invalid arguments. +// 0 No room to send all the commands +// (+) Number of commands sent +//****************************************************************************** +static int +i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, + cmdSyntaxPtr pCs0,...) +{ + int totalsize = 0; + int blocksize; + int lastended; + cmdSyntaxPtr *ppCs; + cmdSyntaxPtr pCs; + int count; + int flag; + i2eBordStrPtr pB; + + unsigned short maxBlock; + unsigned short maxBuff; + short bufroom; + unsigned short stuffIndex; + unsigned char *pBuf; + unsigned char *pInsert; + unsigned char *pDest, *pSource; + unsigned short channel; + int cnt; + unsigned long flags = 0; + spinlock_t *lock_var_p = NULL; + + // Make sure the channel exists, otherwise do nothing + if ( !i2Validate ( pCh ) ) { + return -1; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, ITRC_ENTER, 0 ); +#endif + pB = pCh->pMyBord; + + // Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT + if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == IRQ_UNDEFINED) { + return -2; + } + // If the board has gone fatal, return bad, and also hit the trap routine if + // it exists. + if (pB->i2eFatal) { + if ( pB->i2eFatalTrap ) { + (*(pB)->i2eFatalTrap)(pB); + } + return -3; + } + // Set up some variables, Which buffers are we using? How big are they? + switch(type) + { + case PTYPE_INLINE: + flag = INL; + maxBlock = MAX_OBUF_BLOCK; + maxBuff = OBUF_SIZE; + pBuf = pCh->Obuf; + break; + case PTYPE_BYPASS: + flag = BYP; + maxBlock = MAX_CBUF_BLOCK; + maxBuff = CBUF_SIZE; + pBuf = pCh->Cbuf; + break; + default: + return -4; + } + // Determine the total size required for all the commands + totalsize = blocksize = sizeof(i2CmdHeader); + lastended = 0; + ppCs = &pCs0; + for ( count = nCommands; count; count--, ppCs++) + { + pCs = *ppCs; + cnt = pCs->length; + // Will a new block be needed for this one? + // Two possible reasons: too + // big or previous command has to be at the end of a packet. + if ((blocksize + cnt > maxBlock) || lastended) { + blocksize = sizeof(i2CmdHeader); + totalsize += sizeof(i2CmdHeader); + } + totalsize += cnt; + blocksize += cnt; + + // If this command had to end a block, then we will make sure to + // account for it should there be any more blocks. + lastended = pCs->flags & END; + } + for (;;) { + // Make sure any pending flush commands go out before we add more data. + if ( !( pCh->flush_flags && i2RetryFlushOutput( pCh ) ) ) { + // How much room (this time through) ? + switch(type) { + case PTYPE_INLINE: + lock_var_p = &pCh->Obuf_spinlock; + WRITE_LOCK_IRQSAVE(lock_var_p,flags); + stuffIndex = pCh->Obuf_stuff; + bufroom = pCh->Obuf_strip - stuffIndex; + break; + case PTYPE_BYPASS: + lock_var_p = &pCh->Cbuf_spinlock; + WRITE_LOCK_IRQSAVE(lock_var_p,flags); + stuffIndex = pCh->Cbuf_stuff; + bufroom = pCh->Cbuf_strip - stuffIndex; + break; + default: + return -5; + } + if (--bufroom < 0) { + bufroom += maxBuff; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 2, 1, bufroom ); +#endif + // Check for overflow + if (totalsize <= bufroom) { + // Normal Expected path - We still hold LOCK + break; /* from for()- Enough room: goto proceed */ + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 3, 1, totalsize ); +#endif + // Prepare to wait for buffers to empty + WRITE_UNLOCK_IRQRESTORE(lock_var_p,flags); + serviceOutgoingFifo(pB); // Dump what we got + + if (timeout == 0) { + return 0; // Tired of waiting + } + if (timeout > 0) + timeout--; // So negative values == forever + + if (!in_interrupt()) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; + schedule_timeout(1); // short nap + } else { + // we cannot sched/sleep in interrrupt silly + return 0; + } + if (signal_pending(current)) { + return 0; // Wake up! Time to die!!! + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 4, 0 ); +#endif + } // end of for(;;) + + // At this point we have room and the lock - stick them in. + channel = pCh->infl.hd.i2sChannel; + pInsert = &pBuf[stuffIndex]; // Pointer to start of packet + pDest = CMD_OF(pInsert); // Pointer to start of command + + // When we start counting, the block is the size of the header + for (blocksize = sizeof(i2CmdHeader), count = nCommands, + lastended = 0, ppCs = &pCs0; + count; + count--, ppCs++) + { + pCs = *ppCs; // Points to command protocol structure + + // If this is a bookmark request command, post the fact that a bookmark + // request is pending. NOTE THIS TRICK ONLY WORKS BECAUSE CMD_BMARK_REQ + // has no parameters! The more general solution would be to reference + // pCs->cmd[0]. + if (pCs == CMD_BMARK_REQ) { + pCh->bookMarks++; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 30, 1, pCh->bookMarks ); +#endif + } + cnt = pCs->length; + + // If this command would put us over the maximum block size or + // if the last command had to be at the end of a block, we end + // the existing block here and start a new one. + if ((blocksize + cnt > maxBlock) || lastended) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, 5, 0 ); +#endif + PTYPE_OF(pInsert) = type; + CHANNEL_OF(pInsert) = channel; + // count here does not include the header + CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); + stuffIndex += blocksize; + if(stuffIndex >= maxBuff) { + stuffIndex = 0; + pInsert = pBuf; + } + pInsert = &pBuf[stuffIndex]; // Pointer to start of next pkt + pDest = CMD_OF(pInsert); + blocksize = sizeof(i2CmdHeader); + } + // Now we know there is room for this one in the current block + + blocksize += cnt; // Total bytes in this command + pSource = pCs->cmd; // Copy the command into the buffer + while (cnt--) { + *pDest++ = *pSource++; + } + // If this command had to end a block, then we will make sure to account + // for it should there be any more blocks. + lastended = pCs->flags & END; + } // end for + // Clean up the final block by writing header, etc + + PTYPE_OF(pInsert) = type; + CHANNEL_OF(pInsert) = channel; + // count here does not include the header + CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader); + stuffIndex += blocksize; + if(stuffIndex >= maxBuff) { + stuffIndex = 0; + pInsert = pBuf; + } + // Updates the index, and post the need for service. When adding these to + // the queue of channels, we turn off the interrupt while doing so, + // because at interrupt level we might want to push a channel back to the + // end of the queue. + switch(type) + { + case PTYPE_INLINE: + pCh->Obuf_stuff = stuffIndex; // Store buffer pointer + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + + pB->debugInlineQueued++; + // Add the channel pointer to list of channels needing service (first + // come...), if it's not already there. + i2QueueNeeds(pB, pCh, NEED_INLINE); + break; + + case PTYPE_BYPASS: + pCh->Cbuf_stuff = stuffIndex; // Store buffer pointer + WRITE_UNLOCK_IRQRESTORE(&pCh->Cbuf_spinlock,flags); + + pB->debugBypassQueued++; + // Add the channel pointer to list of channels needing service (first + // come...), if it's not already there. + i2QueueNeeds(pB, pCh, NEED_BYPASS); + break; + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_QUEUE, ITRC_RETURN, 1, nCommands ); +#endif + return nCommands; // Good status: number of commands sent +} + +//****************************************************************************** +// Function: i2GetStatus(pCh,resetBits) +// Parameters: Pointer to a channel structure +// Bit map of status bits to clear +// Returns: Bit map of current status bits +// +// Description: +// Returns the state of data set signals, and whether a break has been received, +// (see i2lib.h for bit-mapped result). resetBits is a bit-map of any status +// bits to be cleared: I2_BRK, I2_PAR, I2_FRA, I2_OVR,... These are cleared +// AFTER the condition is passed. If pCh does not point to a valid channel, +// returns -1 (which would be impossible otherwise. +//****************************************************************************** +static int +i2GetStatus(i2ChanStrPtr pCh, int resetBits) +{ + unsigned short status; + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_STATUS, ITRC_ENTER, 2, pCh->dataSetIn, resetBits ); +#endif + + // Make sure the channel exists, otherwise do nothing */ + if ( !i2Validate ( pCh ) ) + return -1; + + pB = pCh->pMyBord; + + status = pCh->dataSetIn; + + // Clear any specified error bits: but note that only actual error bits can + // be cleared, regardless of the value passed. + if (resetBits) + { + pCh->dataSetIn &= ~(resetBits & (I2_BRK | I2_PAR | I2_FRA | I2_OVR)); + pCh->dataSetIn &= ~(I2_DDCD | I2_DCTS | I2_DDSR | I2_DRI); + } + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_STATUS, ITRC_RETURN, 1, pCh->dataSetIn ); +#endif + + return status; +} + +//****************************************************************************** +// Function: i2Input(pChpDest,count) +// Parameters: Pointer to a channel structure +// Pointer to data buffer +// Number of bytes to read +// Returns: Number of bytes read, or -1 for error +// +// Description: +// Strips data from the input buffer and writes it to pDest. If there is a +// collosal blunder, (invalid structure pointers or the like), returns -1. +// Otherwise, returns the number of bytes read. +//****************************************************************************** +static int +i2Input(i2ChanStrPtr pCh) +{ + int amountToMove; + unsigned short stripIndex; + int count; + unsigned long flags = 0; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, ITRC_ENTER, 0); +#endif + + // Ensure channel structure seems real + if ( !i2Validate( pCh ) ) { + count = -1; + goto i2Input_exit; + } + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + + // initialize some accelerators and private copies + stripIndex = pCh->Ibuf_strip; + + count = pCh->Ibuf_stuff - stripIndex; + + // If buffer is empty or requested data count was 0, (trivial case) return + // without any further thought. + if ( count == 0 ) { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + goto i2Input_exit; + } + // Adjust for buffer wrap + if ( count < 0 ) { + count += IBUF_SIZE; + } + // Don't give more than can be taken by the line discipline + amountToMove = pCh->pTTY->ldisc.receive_room( pCh->pTTY ); + if (count > amountToMove) { + count = amountToMove; + } + // How much could we copy without a wrap? + amountToMove = IBUF_SIZE - stripIndex; + + if (amountToMove > count) { + amountToMove = count; + } + // Move the first block + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + &(pCh->Ibuf[stripIndex]), NULL, amountToMove ); + // If we needed to wrap, do the second data move + if (count > amountToMove) { + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + pCh->Ibuf, NULL, count - amountToMove ); + } + // Bump and wrap the stripIndex all at once by the amount of data read. This + // method is good regardless of whether the data was in one or two pieces. + stripIndex += count; + if (stripIndex >= IBUF_SIZE) { + stripIndex -= IBUF_SIZE; + } + pCh->Ibuf_strip = stripIndex; + + // Update our flow control information and possibly queue ourselves to send + // it, depending on how much data has been stripped since the last time a + // packet was sent. + pCh->infl.asof += count; + + if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) { + pCh->sinceLastFlow -= pCh->whenSendFlow; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); + } else { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + } + +i2Input_exit: + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, ITRC_RETURN, 1, count); +#endif + return count; +} + +//****************************************************************************** +// Function: i2InputFlush(pCh) +// Parameters: Pointer to a channel structure +// Returns: Number of bytes stripped, or -1 for error +// +// Description: +// Strips any data from the input buffer. If there is a collosal blunder, +// (invalid structure pointers or the like), returns -1. Otherwise, returns the +// number of bytes stripped. +//****************************************************************************** +static int +i2InputFlush(i2ChanStrPtr pCh) +{ + int count; + unsigned long flags; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) + return -1; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, 10, 0); +#endif + + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + count = pCh->Ibuf_stuff - pCh->Ibuf_strip; + + // Adjust for buffer wrap + if (count < 0) { + count += IBUF_SIZE; + } + + // Expedient way to zero out the buffer + pCh->Ibuf_strip = pCh->Ibuf_stuff; + + + // Update our flow control information and possibly queue ourselves to send + // it, depending on how much data has been stripped since the last time a + // packet was sent. + + pCh->infl.asof += count; + + if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow ) + { + pCh->sinceLastFlow -= pCh->whenSendFlow; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW); + } else { + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_INPUT, 19, 1, count); +#endif + return count; +} + +//****************************************************************************** +// Function: i2InputAvailable(pCh) +// Parameters: Pointer to a channel structure +// Returns: Number of bytes available, or -1 for error +// +// Description: +// If there is a collosal blunder, (invalid structure pointers or the like), +// returns -1. Otherwise, returns the number of bytes stripped. Otherwise, +// returns the number of bytes available in the buffer. +//****************************************************************************** +#if 0 +static int +i2InputAvailable(i2ChanStrPtr pCh) +{ + int count; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) return -1; + + + // initialize some accelerators and private copies + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags); + count = pCh->Ibuf_stuff - pCh->Ibuf_strip; + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags); + + // Adjust for buffer wrap + if (count < 0) + { + count += IBUF_SIZE; + } + + return count; +} +#endif + +//****************************************************************************** +// Function: i2Output(pCh, pSource, count) +// Parameters: Pointer to channel structure +// Pointer to source data +// Number of bytes to send +// Returns: Number of bytes sent, or -1 for error +// +// Description: +// Queues the data at pSource to be sent as data packets to the board. If there +// is a collosal blunder, (invalid structure pointers or the like), returns -1. +// Otherwise, returns the number of bytes written. What if there is not enough +// room for all the data? If pCh->channelOptions & CO_NBLOCK_WRITE is set, then +// we transfer as many characters as we can now, then return. If this bit is +// clear (default), routine will spin along until all the data is buffered. +// Should this occur, the 1-ms delay routine is called while waiting to avoid +// applications that one cannot break out of. +//****************************************************************************** +static int +i2Output(i2ChanStrPtr pCh, const char *pSource, int count, int user ) +{ + i2eBordStrPtr pB; + unsigned char *pInsert; + int amountToMove; + int countOriginal = count; + unsigned short channel; + unsigned short stuffIndex; + unsigned long flags; + int rc = 0; + + int bailout = 10; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, ITRC_ENTER, 2, count, user ); +#endif + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) + return -1; + + // initialize some accelerators and private copies + pB = pCh->pMyBord; + channel = pCh->infl.hd.i2sChannel; + + // If the board has gone fatal, return bad, and also hit the trap routine if + // it exists. + if (pB->i2eFatal) { + if (pB->i2eFatalTrap) { + (*(pB)->i2eFatalTrap)(pB); + } + return -1; + } + // Proceed as though we would do everything + while ( count > 0 ) { + + // How much room in output buffer is there? + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + if (amountToMove < 0) { + amountToMove += OBUF_SIZE; + } + // Subtract off the headers size and see how much room there is for real + // data. If this is negative, we will discover later. + amountToMove -= sizeof (i2DataHeader); + + // Don't move more (now) than can go in a single packet + if ( amountToMove > (int)(MAX_OBUF_BLOCK - sizeof(i2DataHeader)) ) { + amountToMove = MAX_OBUF_BLOCK - sizeof(i2DataHeader); + } + // Don't move more than the count we were given + if (amountToMove > count) { + amountToMove = count; + } + // Now we know how much we must move: NB because the ring buffers have + // an overflow area at the end, we needn't worry about wrapping in the + // middle of a packet. + +// Small WINDOW here with no LOCK but I can't call Flush with LOCK +// We would be flushing (or ending flush) anyway + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 10, 1, amountToMove ); +#endif + if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) ) + && amountToMove > 0 ) + { + WRITE_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + stuffIndex = pCh->Obuf_stuff; + + // Had room to move some data: don't know whether the block size, + // buffer space, or what was the limiting factor... + pInsert = &(pCh->Obuf[stuffIndex]); + + // Set up the header + CHANNEL_OF(pInsert) = channel; + PTYPE_OF(pInsert) = PTYPE_DATA; + TAG_OF(pInsert) = 0; + ID_OF(pInsert) = ID_ORDINARY_DATA; + DATA_COUNT_OF(pInsert) = amountToMove; + + // Move the data + if ( user ) { + rc=copy_from_user((char*)(DATA_OF(pInsert)), pSource, + amountToMove ); + } else { + memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove ); + } + // Adjust pointers and indices + pSource += amountToMove; + pCh->Obuf_char_count += amountToMove; + stuffIndex += amountToMove + sizeof(i2DataHeader); + count -= amountToMove; + + if (stuffIndex >= OBUF_SIZE) { + stuffIndex = 0; + } + pCh->Obuf_stuff = stuffIndex; + + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex ); +#endif + + } else { + + // Cannot move data + // becuz we need to stuff a flush + // or amount to move is <= 0 + +#ifdef IP2DEBUG_TRACE + ip2trace(CHANN, ITRC_OUTPUT, 14, 3, + amountToMove, pB->i2eFifoRemains, pB->i2eWaitingForEmptyFifo ); +#endif + // Put this channel back on queue + // this ultimatly gets more data or wakes write output + i2QueueNeeds(pB, pCh, NEED_INLINE); + + if ( pB->i2eWaitingForEmptyFifo ) { + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 16, 0 ); +#endif + // or schedule + if (!in_interrupt()) { + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 61, 0 ); +#endif + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; + schedule_timeout(2); + if (signal_pending(current)) { + break; + } + continue; + } else { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 62, 0 ); +#endif + // let interrupt in = WAS restore_flags() + // We hold no lock nor is irq off anymore??? + + break; + } + break; // from while(count) + } + else if ( pB->i2eFifoRemains < 32 && !pB->i2eTxMailEmpty ( pB ) ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 19, 2, pB->i2eFifoRemains, + pB->i2eTxMailEmpty ); +#endif + break; // from while(count) + } else if ( pCh->channelNeeds & NEED_CREDIT ) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 22, 0 ); +#endif + break; // from while(count) + } else if ( --bailout) { + + // Try to throw more things (maybe not us) in the fifo if we're + // not already waiting for it. + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 20, 0 ); +#endif + serviceOutgoingFifo(pB); + //break; CONTINUE; + } else { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 21, 3, pB->i2eFifoRemains, + pB->i2eOutMailWaiting, pB->i2eWaitingForEmptyFifo ); +#endif + break; // from while(count) + } + } + } // End of while(count) + + i2QueueNeeds(pB, pCh, NEED_INLINE); + + // We drop through either when the count expires, or when there is some + // count left, but there was a non-blocking write. + if (countOriginal > count) { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, 17, 2, countOriginal, count ); +#endif + serviceOutgoingFifo( pB ); + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_OUTPUT, ITRC_RETURN, 2, countOriginal, count ); +#endif + + return countOriginal - count; +} + +//****************************************************************************** +// Function: i2FlushOutput(pCh) +// Parameters: Pointer to a channel structure +// Returns: Nothing +// +// Description: +// Sends bypass command to start flushing (waiting possibly forever until there +// is room), then sends inline command to stop flushing output, (again waiting +// possibly forever). +//****************************************************************************** +static inline void +i2FlushOutput(i2ChanStrPtr pCh) +{ + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 1, 1, pCh->flush_flags ); +#endif + + if (pCh->flush_flags) + return; + + if ( 1 != i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { + pCh->flush_flags = STARTFL_FLAG; // Failed - flag for later +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 2, 0 ); +#endif + } else if ( 1 != i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) ) { + pCh->flush_flags = STOPFL_FLAG; // Failed - flag for later +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 3, 0 ); +#endif + } +} + +static int +i2RetryFlushOutput(i2ChanStrPtr pCh) +{ + int old_flags = pCh->flush_flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 14, 1, old_flags ); +#endif + + pCh->flush_flags = 0; // Clear flag so we can avoid recursion + // and queue the commands + + if ( old_flags & STARTFL_FLAG ) { + if ( 1 == i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) { + old_flags = STOPFL_FLAG; //Success - send stop flush + } else { + old_flags = STARTFL_FLAG; //Failure - Flag for retry later + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 15, 1, old_flags ); +#endif + } + if ( old_flags & STOPFL_FLAG ) { + if ( 1 == i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) > 0 ) { + old_flags = 0; // Success - clear flags + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 16, 1, old_flags ); +#endif + } + pCh->flush_flags = old_flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_FLUSH, 17, 1, old_flags ); +#endif + return old_flags; +} + +//****************************************************************************** +// Function: i2DrainOutput(pCh,timeout) +// Parameters: Pointer to a channel structure +// Maximum period to wait +// Returns: ? +// +// Description: +// Uses the bookmark request command to ask the board to send a bookmark back as +// soon as all the data is completely sent. +//****************************************************************************** +static void +i2DrainWakeup(i2ChanStrPtr pCh) +{ +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 10, 1, pCh->BookmarkTimer.expires ); +#endif + pCh->BookmarkTimer.expires = 0; + wake_up_interruptible( &pCh->pBookmarkWait ); +} + +static void +i2DrainOutput(i2ChanStrPtr pCh, int timeout) +{ + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, ITRC_ENTER, 1, pCh->BookmarkTimer.expires); +#endif + pB = pCh->pMyBord; + // If the board has gone fatal, return bad, + // and also hit the trap routine if it exists. + if (pB->i2eFatal) { + if (pB->i2eFatalTrap) { + (*(pB)->i2eFatalTrap)(pB); + } + return; + } + if ((timeout > 0) && (pCh->BookmarkTimer.expires == 0 )) { + // One per customer (channel) + init_timer( &(pCh->BookmarkTimer) ); + pCh->BookmarkTimer.expires = jiffies + timeout; + pCh->BookmarkTimer.function = (void*)(unsigned long)i2DrainWakeup; + pCh->BookmarkTimer.data = (unsigned long)pCh; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 1, 1, pCh->BookmarkTimer.expires ); +#endif + + add_timer( &(pCh->BookmarkTimer) ); + } + + i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ ); + serviceOutgoingFifo( pB ); + + interruptible_sleep_on( &(pCh->pBookmarkWait) ); + + // if expires == 0 then timer poped, then do not need to del_timer + if ((timeout > 0) && pCh->BookmarkTimer.expires && + (pCh->BookmarkTimer.expires > jiffies)) { + del_timer( &(pCh->BookmarkTimer) ); + pCh->BookmarkTimer.expires = 0; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, 3, 1, pCh->BookmarkTimer.expires ); +#endif + + } +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_DRAIN, ITRC_RETURN, 1, pCh->BookmarkTimer.expires ); +#endif + return; +} + +//****************************************************************************** +// Function: i2OutputFree(pCh) +// Parameters: Pointer to a channel structure +// Returns: Space in output buffer +// +// Description: +// Returns -1 if very gross error. Otherwise returns the amount of bytes still +// free in the output buffer. +//****************************************************************************** +static int +i2OutputFree(i2ChanStrPtr pCh) +{ + int amountToMove; + unsigned long flags; + + // Ensure channel structure seems real + if ( !i2Validate ( pCh ) ) { + return -1; + } + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + + if (amountToMove < 0) { + amountToMove += OBUF_SIZE; + } + // If this is negative, we will discover later + amountToMove -= sizeof(i2DataHeader); + + return (amountToMove < 0) ? 0 : amountToMove; +} +static void + +ip2_owake( PTTY tp) +{ + i2ChanStrPtr pCh; + + if (tp == NULL) return; + + pCh = tp->driver_data; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags, (1 << TTY_DO_WRITE_WAKEUP) ); +#endif + wake_up_interruptible ( &tp->write_wait ); + if ( ( tp->flags & (1 << TTY_DO_WRITE_WAKEUP) ) + && tp->ldisc.write_wakeup ) + { + (tp->ldisc.write_wakeup) ( tp ); +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 11, 0 ); +#endif + } +} + +static inline void +set_baud_params(i2eBordStrPtr pB) +{ + int i,j; + i2ChanStrPtr *pCh; + + pCh = (i2ChanStrPtr *) pB->i2eChannelPtr; + + for (i = 0; i < ABS_MAX_BOXES; i++) { + if (pB->channelBtypes.bid_value[i]) { + if (BID_HAS_654(pB->channelBtypes.bid_value[i])) { + for (j = 0; j < ABS_BIGGEST_BOX; j++) { + if (pCh[i*16+j] == NULL) + break; + (pCh[i*16+j])->BaudBase = 921600; // MAX for ST654 + (pCh[i*16+j])->BaudDivisor = 96; + } + } else { // has cirrus cd1400 + for (j = 0; j < ABS_BIGGEST_BOX; j++) { + if (pCh[i*16+j] == NULL) + break; + (pCh[i*16+j])->BaudBase = 115200; // MAX for CD1400 + (pCh[i*16+j])->BaudDivisor = 12; + } + } + } + } +} + +//****************************************************************************** +// Function: i2StripFifo(pB) +// Parameters: Pointer to a board structure +// Returns: ? +// +// Description: +// Strips all the available data from the incoming FIFO, identifies the type of +// packet, and either buffers the data or does what needs to be done. +// +// Note there is no overflow checking here: if the board sends more data than it +// ought to, we will not detect it here, but blindly overflow... +//****************************************************************************** + +// A buffer for reading in blocks for unknown channels +static unsigned char junkBuffer[IBUF_SIZE]; + +// A buffer to read in a status packet. Because of the size of the count field +// for these things, the maximum packet size must be less than MAX_CMD_PACK_SIZE +static unsigned char cmdBuffer[MAX_CMD_PACK_SIZE + 4]; + +// This table changes the bit order from MSR order given by STAT_MODEM packet to +// status bits used in our library. +static char xlatDss[16] = { +0 | 0 | 0 | 0 , +0 | 0 | 0 | I2_CTS , +0 | 0 | I2_DSR | 0 , +0 | 0 | I2_DSR | I2_CTS , +0 | I2_RI | 0 | 0 , +0 | I2_RI | 0 | I2_CTS , +0 | I2_RI | I2_DSR | 0 , +0 | I2_RI | I2_DSR | I2_CTS , +I2_DCD | 0 | 0 | 0 , +I2_DCD | 0 | 0 | I2_CTS , +I2_DCD | 0 | I2_DSR | 0 , +I2_DCD | 0 | I2_DSR | I2_CTS , +I2_DCD | I2_RI | 0 | 0 , +I2_DCD | I2_RI | 0 | I2_CTS , +I2_DCD | I2_RI | I2_DSR | 0 , +I2_DCD | I2_RI | I2_DSR | I2_CTS }; + +static inline void +i2StripFifo(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + int channel; + int count; + unsigned short stuffIndex; + int amountToRead; + unsigned char *pc, *pcLimit; + unsigned char uc; + unsigned char dss_change; + unsigned long bflags,cflags; + +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 ); +#endif + + while (HAS_INPUT(pB)) { +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 ); +#endif + // Process packet from fifo a one atomic unit + WRITE_LOCK_IRQSAVE(&pB->read_fifo_spinlock,bflags); + + // The first word (or two bytes) will have channel number and type of + // packet, possibly other information + pB->i2eLeadoffWord[0] = iiReadWord(pB); + + switch(PTYPE_OF(pB->i2eLeadoffWord)) + { + case PTYPE_DATA: + pB->got_input = 1; + +#ifdef IP2DEBUG_TRACE + //ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 3, 0 ); +#endif + channel = CHANNEL_OF(pB->i2eLeadoffWord); /* Store channel */ + count = iiReadWord(pB); /* Count is in the next word */ + +// NEW: Check the count for sanity! Should the hardware fail, our death +// is more pleasant. While an oversize channel is acceptable (just more +// than the driver supports), an over-length count clearly means we are +// sick! + if ( ((unsigned int)count) > IBUF_SIZE ) { + pB->i2eFatal = 2; + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + return; /* Bail out ASAP */ + } + // Channel is illegally big ? + if (channel >= pB->i2eChannelCnt || + (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) == NULL) + { + iiReadBuf(pB, junkBuffer, count); + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + break; /* From switch: ready for next packet */ + } + + // Channel should be valid, then + + // If this is a hot-key, merely post its receipt for now. These are + // always supposed to be 1-byte packets, so we won't even check the + // count. Also we will post an acknowledgement to the board so that + // more data can be forthcoming. Note that we are not trying to use + // these sequences in this driver, merely to robustly ignore them. + if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY) + { + pCh->hotKeyIn = iiReadWord(pB) & 0xff; + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK); + break; /* From the switch: ready for next packet */ + } + + // Normal data! We crudely assume there is room for the data in our + // buffer because the board wouldn't have exceeded his credit limit. + WRITE_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,cflags);// We have 2 locks now + stuffIndex = pCh->Ibuf_stuff; + amountToRead = IBUF_SIZE - stuffIndex; + if (amountToRead > count) + amountToRead = count; + + // stuffIndex would have been already adjusted so there would + // always be room for at least one, and count is always at least + // one. + + iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); + + // Update the stuffIndex by the amount of data moved. Note we could + // never ask for more data than would just fit. However, we might + // have read in one more byte than we wanted because the read + // rounds up to even bytes. If this byte is on the end of the + // packet, and is padding, we ignore it. If the byte is part of + // the actual data, we need to move it. + + stuffIndex += amountToRead; + + if (stuffIndex >= IBUF_SIZE) { + if ((amountToRead & 1) && (count > amountToRead)) { + pCh->Ibuf[0] = pCh->Ibuf[IBUF_SIZE]; + amountToRead++; + stuffIndex = 1; + } else { + stuffIndex = 0; + } + } + + // If there is anything left over, read it as well + if (count > amountToRead) { + amountToRead = count - amountToRead; + iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead); + stuffIndex += amountToRead; + } + + // Update stuff index + pCh->Ibuf_stuff = stuffIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,cflags); + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + +#ifdef USE_IQ + queue_task(&pCh->tqueue_input, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + do_input(pCh); +#endif + + // Note we do not need to maintain any flow-control credits at this + // time: if we were to increment .asof and decrement .room, there + // would be no net effect. Instead, when we strip data, we will + // increment .asof and leave .room unchanged. + + break; // From switch: ready for next packet + + case PTYPE_STATUS: +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 4, 0 ); +#endif + + count = CMD_COUNT_OF(pB->i2eLeadoffWord); + + iiReadBuf(pB, cmdBuffer, count); + // We can release early with buffer grab + WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags); + + pc = cmdBuffer; + pcLimit = &(cmdBuffer[count]); + + while (pc < pcLimit) { + channel = *pc++; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_SFIFO, 7, 2, channel, *pc ); +#endif + /* check for valid channel */ + if (channel < pB->i2eChannelCnt + && + (pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) != NULL + ) + { + dss_change = 0; + + switch (uc = *pc++) + { + /* Breaks and modem signals are easy: just update status */ + case STAT_CTS_UP: + if ( !(pCh->dataSetIn & I2_CTS) ) + { + pCh->dataSetIn |= I2_DCTS; + ++pCh->icount.cts; + dss_change = 1; + } + pCh->dataSetIn |= I2_CTS; + break; + + case STAT_CTS_DN: + if ( pCh->dataSetIn & I2_CTS ) + { + pCh->dataSetIn |= I2_DCTS; + ++pCh->icount.cts; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_CTS; + break; + + case STAT_DCD_UP: +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 1, 1, pCh->dataSetIn ); +#endif + if ( !(pCh->dataSetIn & I2_DCD) ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_MODEM, 2, 0 ); +#endif + pCh->dataSetIn |= I2_DDCD; + ++pCh->icount.dcd; + dss_change = 1; + } + pCh->dataSetIn |= I2_DCD; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 3, 1, pCh->dataSetIn ); +#endif + break; + + case STAT_DCD_DN: +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 4, 1, pCh->dataSetIn ); +#endif + if ( pCh->dataSetIn & I2_DCD ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 5, 0 ); +#endif + pCh->dataSetIn |= I2_DDCD; + ++pCh->icount.dcd; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_DCD; +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_MODEM, 6, 1, pCh->dataSetIn ); +#endif + break; + + case STAT_DSR_UP: + if ( !(pCh->dataSetIn & I2_DSR) ) + { + pCh->dataSetIn |= I2_DDSR; + ++pCh->icount.dsr; + dss_change = 1; + } + pCh->dataSetIn |= I2_DSR; + break; + + case STAT_DSR_DN: + if ( pCh->dataSetIn & I2_DSR ) + { + pCh->dataSetIn |= I2_DDSR; + ++pCh->icount.dsr; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_DSR; + break; + + case STAT_RI_UP: + if ( !(pCh->dataSetIn & I2_RI) ) + { + pCh->dataSetIn |= I2_DRI; + ++pCh->icount.rng; + dss_change = 1; + } + pCh->dataSetIn |= I2_RI ; + break; + + case STAT_RI_DN: + if ( pCh->dataSetIn & I2_RI ) + { + pCh->dataSetIn |= I2_DRI; + dss_change = 1; + } + pCh->dataSetIn &= ~I2_RI ; + break; + + case STAT_BRK_DET: + pCh->dataSetIn |= I2_BRK; + dss_change = 1; + break; + + // Bookmarks? one less request we're waiting for + case STAT_BMARK: + pCh->bookMarks--; + if (pCh->bookMarks <= 0 ) { + pCh->bookMarks = 0; + wake_up_interruptible( &pCh->pBookmarkWait ); +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_DRAIN, 20, 1, pCh->BookmarkTimer.expires ); +#endif + } + break; + + // Flow control packets? Update the new credits, and if + // someone was waiting for output, queue him up again. + case STAT_FLOW: + pCh->outfl.room = + ((flowStatPtr)pc)->room - + (pCh->outfl.asof - ((flowStatPtr)pc)->asof); +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 1, 1, pCh->outfl.room ); +#endif + if (pCh->channelNeeds & NEED_CREDIT) + { +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 2, 1, pCh->channelNeeds); +#endif + pCh->channelNeeds &= ~NEED_CREDIT; + i2QueueNeeds(pB, pCh, NEED_INLINE); + if ( pCh->pTTY ) + ip2_owake(pCh->pTTY); + } +#ifdef IP2DEBUG_TRACE + ip2trace (channel, ITRC_STFLW, 3, 1, pCh->channelNeeds); +#endif + pc += sizeof(flowStat); + break; + + /* Special packets: */ + /* Just copy the information into the channel structure */ + + case STAT_STATUS: + + pCh->channelStatus = *((debugStatPtr)pc); + pc += sizeof(debugStat); + break; + + case STAT_TXCNT: + + pCh->channelTcount = *((cntStatPtr)pc); + pc += sizeof(cntStat); + break; + + case STAT_RXCNT: + + pCh->channelRcount = *((cntStatPtr)pc); + pc += sizeof(cntStat); + break; + + case STAT_BOXIDS: + pB->channelBtypes = *((bidStatPtr)pc); + pc += sizeof(bidStat); +//printk("boxids: %x %x %x %x\n", +// pB->channelBtypes.bid_value[0],pB->channelBtypes.bid_value[1], +// pB->channelBtypes.bid_value[2],pB->channelBtypes.bid_value[3]); + set_baud_params(pB); + break; + + case STAT_HWFAIL: + i2QueueCommands (PTYPE_INLINE, pCh, 0, 1, CMD_HW_TEST); + pCh->channelFail = *((failStatPtr)pc); + pc += sizeof(failStat); + break; + + /* No explicit match? then + * Might be an error packet... + */ + default: + switch (uc & STAT_MOD_ERROR) + { + case STAT_ERROR: + if (uc & STAT_E_PARITY) + pCh->dataSetIn |= I2_PAR; + if (uc & STAT_E_FRAMING) + pCh->dataSetIn |= I2_FRA; + if (uc & STAT_E_OVERRUN) + pCh->dataSetIn |= I2_OVR; + break; + + case STAT_MODEM: + pCh->dataSetIn = (pCh->dataSetIn + & ~(I2_RI | I2_CTS | I2_DCD | I2_DSR) ) + | xlatDss[uc & 0xf]; + default: + break; + } + } /* End of switch on status type */ + if (dss_change) { +#ifdef USE_IQ + queue_task(&pCh->tqueue_status, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + do_status(pCh); +#endif + } + } + else /* Or else, channel is invalid */ + { + // Even though the channel is invalid, we must test the + // status to see how much additional data it has (to be + // skipped) + switch (*pc++) + { + case STAT_FLOW: + pc += 4; /* Skip the data */ + break; + + case STAT_CTS_UP: + case STAT_CTS_DN: + case STAT_DCD_UP: + case STAT_DCD_DN: + case STAT_DSR_UP: + case STAT_DSR_DN: + case STAT_RI_UP: + case STAT_RI_DN: + case STAT_BRK_DET: + case STAT_BMARK: + default: + break; + } + } + } // End of while (there is still some status packet left) + break; + + default: // Neither packet? should be impossible +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1, + PTYPE_OF(pB->i2eLeadoffWord) ); +#endif + break; + } // End of switch on type of packets + } //while(board HAS_INPUT) +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 ); +#endif + // Send acknowledgement to the board even if there was no data! + pB->i2eOutMailWaiting |= MB_IN_STRIPPED; + return; +} + +//****************************************************************************** +// Function: i2Write2Fifo(pB,address,count) +// Parameters: Pointer to a board structure, source address, byte count +// Returns: bytes written +// +// Description: +// Writes count bytes to board io address(implied) from source +// Adjusts count, leaves reserve for next time around bypass cmds +//****************************************************************************** +static int +i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve) +{ + int rc = 0; + unsigned long flags; + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + if (!pB->i2eWaitingForEmptyFifo) { + if (pB->i2eFifoRemains > (count+reserve)) { + pB->i2eFifoRemains -= count; + iiWriteBuf(pB, source, count); + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + rc = count; + } + } + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + return rc; +} +//****************************************************************************** +// Function: i2StuffFifoBypass(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as many bypass commands into the fifo as possible. This is simpler +// than stuffing data or inline commands to fifo, since we do not have +// flow-control to deal with. +//****************************************************************************** +static inline void +i2StuffFifoBypass(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned char *pRemove; + unsigned short stripIndex; + unsigned short packetSize; + unsigned short paddedSize; + unsigned short notClogged = 1; + unsigned long flags; + + int bailout = 1000; + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( --bailout && notClogged && + (NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS)))) + { + WRITE_LOCK_IRQSAVE(&pCh->Cbuf_spinlock,flags); + stripIndex = pCh->Cbuf_strip; + + // as long as there are packets for this channel... + + while (stripIndex != pCh->Cbuf_stuff) { + pRemove = &(pCh->Cbuf[stripIndex]); + packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader); + paddedSize = ROUNDUP(packetSize); + + if (paddedSize > 0) { + if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) { + notClogged = 0; /* fifo full */ + i2QueueNeeds(pB, pCh, NEED_BYPASS); // Put back on queue + break; // Break from the channel + } + } +#ifdef DEBUG_FIFO +WriteDBGBuf("BYPS", pRemove, paddedSize); +#endif /* DEBUG_FIFO */ + pB->debugBypassCount++; + + pRemove += packetSize; + stripIndex += packetSize; + if (stripIndex >= CBUF_SIZE) { + stripIndex = 0; + pRemove = pCh->Cbuf; + } + } + // Done with this channel. Move to next, removing this one from + // the queue of channels if we cleaned it out (i.e., didn't get clogged. + pCh->Cbuf_strip = stripIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Cbuf_spinlock,flags); + } // Either clogged or finished all the work + +#ifdef IP2DEBUG_TRACE + if ( !bailout ) { + ip2trace (ITRC_NO_PORT, ITRC_ERROR, 1, 0 ); + } +#endif +} + +//****************************************************************************** +// Function: i2StuffFifoFlow(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as many flow control packets into the fifo as possible. This is easier +// even than doing normal bypass commands, because there is always at most one +// packet, already assembled, for each channel. +//****************************************************************************** +static inline void +i2StuffFifoFlow(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned short paddedSize = ROUNDUP(sizeof(flowIn)); + +#ifdef IP2DEBUG_TRACE +ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2, pB->i2eFifoRemains, paddedSize ); +#endif + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( (NULL != (pCh = i2DeQueueNeeds(pB,NEED_FLOW)))) { + pB->debugFlowCount++; + + // NO Chan LOCK needed ??? + if ( 0 == i2Write2Fifo(pB,(unsigned char *)&(pCh->infl),paddedSize,0)) { + break; + } +#ifdef DEBUG_FIFO +WriteDBGBuf("FLOW",(unsigned char *) &(pCh->infl), paddedSize); +#endif /* DEBUG_FIFO */ + + } // Either clogged or finished all the work + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_RETURN, 0 ); +#endif +} + +//****************************************************************************** +// Function: i2StuffFifoInline(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Stuffs as much data and inline commands into the fifo as possible. This is +// the most complex fifo-stuffing operation, since there if now the channel +// flow-control issue to deal with. +//****************************************************************************** +static inline void +i2StuffFifoInline(i2eBordStrPtr pB) +{ + i2ChanStrPtr pCh; + unsigned char *pRemove; + unsigned short stripIndex; + unsigned short packetSize; + unsigned short paddedSize; + unsigned short notClogged = 1; + unsigned short flowsize; + unsigned long flags; + + int bailout = 1000; + int bailout2; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_ENTER, 3, pB->i2eFifoRemains, + pB->i2Dbuf_strip, pB->i2Dbuf_stuff ); +#endif + + // Continue processing so long as there are entries, or there is room in the + // fifo. Each entry represents a channel with something to do. + while ( --bailout && notClogged && + (NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) ) + { + WRITE_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + stripIndex = pCh->Obuf_strip; + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff ); +#endif + // as long as there are packets for this channel... + bailout2 = 1000; + while ( --bailout2 && stripIndex != pCh->Obuf_stuff) { + pRemove = &(pCh->Obuf[stripIndex]); + + // Must determine whether this be a data or command packet to + // calculate correctly the header size and the amount of + // flow-control credit this type of packet will use. + if (PTYPE_OF(pRemove) == PTYPE_DATA) { + flowsize = DATA_COUNT_OF(pRemove); + packetSize = flowsize + sizeof(i2DataHeader); + } else { + flowsize = CMD_COUNT_OF(pRemove); + packetSize = flowsize + sizeof(i2CmdHeader); + } + flowsize = CREDIT_USAGE(flowsize); + paddedSize = ROUNDUP(packetSize); + +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize ); +#endif + // If we don't have enough credits from the board to send the data, + // flag the channel that we are waiting for flow control credit, and + // break out. This will clean up this channel and remove us from the + // queue of hot things to do. +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 5, 2, pCh->outfl.room, flowsize ); +#endif + if (pCh->outfl.room <= flowsize) { + // Do Not have the credits to send this packet. + i2QueueNeeds(pB, pCh, NEED_CREDIT); + notClogged = 0; + break; // So to do next channel + } + if ( (paddedSize > 0) + && ( 0 == i2Write2Fifo(pB, pRemove, paddedSize, 128))) { + // Do Not have room in fifo to send this packet. + notClogged = 0; + i2QueueNeeds(pB, pCh, NEED_INLINE); + break; // Break from the channel + } +#ifdef DEBUG_FIFO +WriteDBGBuf("DATA", pRemove, paddedSize); +#endif /* DEBUG_FIFO */ + pB->debugInlineCount++; + + // Update current credits + pCh->outfl.room -= flowsize; + pCh->outfl.asof += flowsize; + if (PTYPE_OF(pRemove) == PTYPE_DATA) { + pCh->Obuf_char_count -= DATA_COUNT_OF(pRemove); + } + pRemove += packetSize; + stripIndex += packetSize; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 6, 2, stripIndex, pCh->Obuf_strip); +#endif + if (stripIndex >= OBUF_SIZE) { + stripIndex = 0; + pRemove = pCh->Obuf; +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 7, 1, stripIndex ); +#endif + } + } /* while */ + if ( !bailout2 ) { + ip2trace (CHANN, ITRC_ERROR, 3, 0 ); + } + // Done with this channel. Move to next, removing this one from the + // queue of channels if we cleaned it out (i.e., didn't get clogged. + pCh->Obuf_strip = stripIndex; + WRITE_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + if ( notClogged ) + { +#ifdef IP2DEBUG_TRACE + ip2trace (CHANN, ITRC_SICMD, 8, 0 ); +#endif + if ( pCh->pTTY ) { + ip2_owake(pCh->pTTY); + } + } + } // Either clogged or finished all the work +#ifdef IP2DEBUG_TRACE + if ( !bailout ) { + ip2trace (ITRC_NO_PORT, ITRC_ERROR, 4, 0 ); + } +#endif + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_RETURN, 1,pB->i2Dbuf_strip); +#endif +} + +//****************************************************************************** +// Function: serviceOutgoingFifo(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Helper routine to put data in the outgoing fifo, if we aren't already waiting +// for something to be there. If the fifo has only room for a very little data, +// go head and hit the board with a mailbox hit immediately. Otherwise, it will +// have to happen later in the interrupt processing. Since this routine may be +// called both at interrupt and foreground time, we must turn off interrupts +// during the entire process. +//****************************************************************************** +static void +serviceOutgoingFifo(i2eBordStrPtr pB) +{ + // If we aren't currently waiting for the board to empty our fifo, service + // everything that is pending, in priority order (especially, Bypass before + // Inline). + if ( ! pB->i2eWaitingForEmptyFifo ) + { + i2StuffFifoFlow(pB); + i2StuffFifoBypass(pB); + i2StuffFifoInline(pB); + + iiSendPendingMail(pB); + } +} + +//****************************************************************************** +// Function: i2ServiceBoard(pB) +// Parameters: Pointer to a board structure +// Returns: Nothing +// +// Description: +// Normally this is called from interrupt level, but there is deliberately +// nothing in here specific to being called from interrupt level. All the +// hardware-specific, interrupt-specific things happen at the outer levels. +// +// For example, a timer interrupt could drive this routine for some sort of +// polled operation. The only requirement is that the programmer deal with any +// atomiticity/concurrency issues that result. +// +// This routine responds to the board's having sent mailbox information to the +// host (which would normally cause an interrupt). This routine reads the +// incoming mailbox. If there is no data in it, this board did not create the +// interrupt and/or has nothing to be done to it. (Except, if we have been +// waiting to write mailbox data to it, we may do so. +// +// Based on the value in the mailbox, we may take various actions. +// +// No checking here of pB validity: after all, it shouldn't have been called by +// the handler unless pB were on the list. +//****************************************************************************** +static inline int +i2ServiceBoard ( i2eBordStrPtr pB ) +{ + unsigned inmail; + unsigned long flags; + + + inmail = iiGetMail(pB); + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail ); +#endif + + if (inmail != NO_MAIL_HERE) { + // If the board has gone fatal, nothing to do but hit a bit that will + // alert foreground tasks to protest! + if ( inmail & MB_FATAL_ERROR ) { + pB->i2eFatal = 1; + goto exit_i2ServiceBoard; + } + + /* Assuming no fatal condition, we proceed to do work */ + if ( inmail & MB_IN_STUFFED ) { + pB->i2eFifoInInts++; + i2StripFifo(pB); /* There might be incoming packets */ + } + + if (inmail & MB_OUT_STRIPPED) { + pB->i2eFifoOutInts++; + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + pB->i2eFifoRemains = pB->i2eFifoSize; + pB->i2eWaitingForEmptyFifo = 0; + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains ); +#endif + } + serviceOutgoingFifo(pB); + } + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 8, 0 ); +#endif + +exit_i2ServiceBoard: + + return 0; +} diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2lib.h linux/drivers/char/ip2/i2lib.h --- v2.2.11/linux/drivers/char/ip2/i2lib.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2lib.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,350 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Header file for high level library functions +* +*******************************************************************************/ +#ifndef I2LIB_H +#define I2LIB_H 1 +//------------------------------------------------------------------------------ +// I2LIB.H +// +// IntelliPort-II and IntelliPort-IIEX +// +// Defines, structure definitions, and external declarations for i2lib.c +//------------------------------------------------------------------------------ +//-------------------------------------- +// Mandatory Includes: +//-------------------------------------- +#include "ip2types.h" +#include "i2ellis.h" +#include "i2pack.h" +#include "i2cmd.h" + +//------------------------------------------------------------------------------ +// i2ChanStr -- Channel Structure: +// Used to track per-channel information for the library routines using standard +// loadware. Note also, a pointer to an array of these structures is patched +// into the i2eBordStr (see i2ellis.h) +//------------------------------------------------------------------------------ +// +// If we make some limits on the maximum block sizes, we can avoid dealing with +// buffer wrap. The wrapping of the buffer is based on where the start of the +// packet is. Then there is always room for the packet contiguously. +// +// Maximum total length of an outgoing data or in-line command block. The limit +// of 36 on data is quite arbitrary and based more on DOS memory limitations +// than the board interface. However, for commands, the maximum packet length is +// MAX_CMD_PACK_SIZE, because the field size for the count is only a few bits +// (see I2PACK.H) in such packets. For data packets, the count field size is not +// the limiting factor. As of this writing, MAX_OBUF_BLOCK < MAX_CMD_PACK_SIZE, +// but be careful if wanting to modify either. +// +#define MAX_OBUF_BLOCK 36 + +// Another note on maximum block sizes: we are buffering packets here. Data is +// put into the buffer (if there is room) regardless of the credits from the +// board. The board sends new credits whenever it has removed from his buffers a +// number of characters equal to 80% of total buffer size. (Of course, the total +// buffer size is what is reported when the very first set of flow control +// status packets are received from the board. Therefore, to be robust, you must +// always fill the board to at least 80% of the current credit limit, else you +// might not give it enough to trigger a new report. These conditions are +// obtained here so long as the maximum output block size is less than 20% the +// size of the board's output buffers. This is true at present by "coincidence" +// or "infernal knowledge": the board's output buffers are at least 700 bytes +// long (20% = 140 bytes, at least). The 80% figure is "official", so the safest +// strategy might be to trap the first flow control report and guarantee that +// the effective maxObufBlock is the minimum of MAX_OBUF_BLOCK and 20% of first +// reported buffer credit. +// +#define MAX_CBUF_BLOCK 6 // Maximum total length of a bypass command block + +#define IBUF_SIZE 500 // character capacity of input buffer per channel +#define OBUF_SIZE 2048// character capacity of output buffer per channel +#define CBUF_SIZE 10 // character capacity of output bypass buffer + +typedef struct _i2ChanStr +{ + // First, back-pointers so that given a pointer to this structure, you can + // determine the correct board and channel number to reference, (say, when + // issuing commands, etc. (Note, channel number is in infl.hd.i2sChannel.) + + int port_index; // Index of port in channel structure array attached + // to board structure. + PTTY pTTY; // Pointer to tty structure for port (OS specific) + USHORT validity; // Indicates whether the given channel has been + // initialized, really exists (or is a missing + // channel, e.g. channel 9 on an 8-port box.) + + i2eBordStrPtr pMyBord; // Back-pointer to this channel's board structure + + int wopen; // waiting fer carrier + + int throttled; // Set if upper layer can take no data + + int flags; // Defined in tty.h + int session; // Defined in tty.h + int pgrp; // Defined in tty.h + + PWAITQ open_wait; // Pointer for OS sleep function. + PWAITQ close_wait; // Pointer for OS sleep function. + PWAITQ delta_msr_wait;// Pointer for OS sleep function. + + struct timer_list BookmarkTimer; // Used by i2DrainOutput + struct wait_queue *pBookmarkWait; // Used by i2DrainOutput + + struct termios NormalTermios; + struct termios CalloutTermios; + + int BaudBase; + int BaudDivisor; + + USHORT ClosingDelay; + USHORT ClosingWaitTime; + + volatile + flowIn infl; // This structure is initialized as a completely + // formed flow-control command packet, and as such + // has the channel number, also the capacity and + // "as-of" data needed continuously. + + USHORT sinceLastFlow; // Counts the number of characters read from input + // buffers, since the last time flow control info + // was sent. + + USHORT whenSendFlow; // Determines when new flow control is to be sent to + // the board. Note unlike earlier manifestations of + // the driver, these packets can be sent from + // in-place. + + USHORT channelNeeds; // Bit map of important things which must be done + // for this channel. (See bits below ) + + volatile + flowStat outfl; // Same type of structure is used to hold current + // flow control information used to control our + // output. "asof" is kept updated as data is sent, + // and "room" never goes to zero. + + // The incoming ring buffer + // Unlike the outgoing buffers, this holds raw data, not packets. The two + // extra bytes are used to hold the byte-padding when there is room for an + // odd number of bytes before we must wrap. + // + UCHAR Ibuf[IBUF_SIZE + 2]; + volatile + USHORT Ibuf_stuff; // Stuffing index + volatile + USHORT Ibuf_strip; // Stripping index + + // The outgoing ring-buffer: Holds Data and command packets. N.B., even + // though these are in the channel structure, the channel is also written + // here, the easier to send it to the fifo when ready. HOWEVER, individual + // packets here are NOT padded to even length: the routines for writing + // blocks to the the fifo will pad to even byte counts. + // + UCHAR Obuf[OBUF_SIZE+MAX_OBUF_BLOCK+4]; + volatile + USHORT Obuf_stuff; // Stuffing index + volatile + USHORT Obuf_strip; // Stripping index + int Obuf_char_count; + + // The outgoing bypass-command buffer. Unlike earlier manifestations, the + // flow control packets are sent directly from the structures. As above, the + // channel number is included in the packet, but they are NOT padded to even + // size. + // + UCHAR Cbuf[CBUF_SIZE+MAX_CBUF_BLOCK+2]; + volatile + USHORT Cbuf_stuff; // Stuffing index + volatile + USHORT Cbuf_strip; // Stripping index + + // The temporary buffer for the Linux tty driver PutChar entry. + // + UCHAR Pbuf[MAX_OBUF_BLOCK - sizeof (i2DataHeader)]; + volatile + USHORT Pbuf_stuff; // Stuffing index + + // The state of incoming data-set signals + // + USHORT dataSetIn; // Bit-mapped according to below. Also indicates + // whether a break has been detected since last + // inquiry. + + // The state of outcoming data-set signals (as far as we can tell!) + // + USHORT dataSetOut; // Bit-mapped according to below. + + // Most recent hot-key identifier detected + // + USHORT hotKeyIn; // Hot key as sent by the board, HOT_CLEAR indicates + // no hot key detected since last examined. + + // Counter of outstanding requests for bookmarks + // + short bookMarks; // Number of outstanding bookmark requests, (+ive + // whenever a bookmark request if queued up, -ive + // whenever a bookmark is received). + + // Misc options + // + USHORT channelOptions; // See below + + // To store various incoming special packets + // + debugStat channelStatus; + cntStat channelRcount; + cntStat channelTcount; + failStat channelFail; + + // To store the last values for line characteristics we sent to the board. + // + int speed; + + int flush_flags; + + void (*trace)(unsigned short,unsigned char,unsigned char,unsigned long,...); + +#ifdef __KERNEL__ + /* + * Kernel counters for the 4 input interrupts + */ + struct async_icount icount; + + /* + * Task queues for processing input packets from the board. + */ + struct tq_struct tqueue_input; + struct tq_struct tqueue_status; + struct tq_struct tqueue_hangup; +#endif + + spinlock_t Ibuf_spinlock; + spinlock_t Obuf_spinlock; + spinlock_t Cbuf_spinlock; + spinlock_t Pbuf_spinlock; + +} i2ChanStr, *i2ChanStrPtr; + +//--------------------------------------------------- +// Manifests and bit-maps for elements in i2ChanStr +//--------------------------------------------------- +// +// flush flags +// +#define STARTFL_FLAG 1 +#define STOPFL_FLAG 2 + +// validity +// +#define CHANNEL_MAGIC_BITS 0xff00 +#define CHANNEL_MAGIC 0x5300 // (validity & CHANNEL_MAGIC_BITS) == + // CHANNEL_MAGIC --> structure good + +#define CHANNEL_SUPPORT 0x0001 // Indicates channel is supported, exists, + // and passed P.O.S.T. + +// channelNeeds +// +#define NEED_FLOW 1 // Indicates flow control has been queued +#define NEED_INLINE 2 // Indicates inline commands or data queued +#define NEED_BYPASS 4 // Indicates bypass commands queued +#define NEED_CREDIT 8 // Indicates would be sending except has not sufficient + // credit. The data is still in the channel structure, + // but the channel is not enqueued in the board + // structure again until there is a credit received from + // the board. + +// dataSetIn (Also the bits for i2GetStatus return value) +// +#define I2_DCD 1 +#define I2_CTS 2 +#define I2_DSR 4 +#define I2_RI 8 + +// dataSetOut (Also the bits for i2GetStatus return value) +// +#define I2_DTR 1 +#define I2_RTS 2 + +// i2GetStatus() can optionally clear these bits +// +#define I2_BRK 0x10 // A break was detected +#define I2_PAR 0x20 // A parity error was received +#define I2_FRA 0x40 // A framing error was received +#define I2_OVR 0x80 // An overrun error was received + +// i2GetStatus() automatically clears these bits */ +// +#define I2_DDCD 0x100 // DCD changed from its former value +#define I2_DCTS 0x200 // CTS changed from its former value +#define I2_DDSR 0x400 // DSR changed from its former value +#define I2_DRI 0x800 // RI changed from its former value + +// hotKeyIn +// +#define HOT_CLEAR 0x1322 // Indicates that no hot-key has been detected + +// channelOptions +// +#define CO_NBLOCK_WRITE 1 // Writes don't block waiting for buffer. (Default + // is, they do wait.) + +// fcmodes +// +#define I2_OUTFLOW_CTS 0x0001 +#define I2_INFLOW_RTS 0x0002 +#define I2_INFLOW_DSR 0x0004 +#define I2_INFLOW_DTR 0x0008 +#define I2_OUTFLOW_DSR 0x0010 +#define I2_OUTFLOW_DTR 0x0020 +#define I2_OUTFLOW_XON 0x0040 +#define I2_OUTFLOW_XANY 0x0080 +#define I2_INFLOW_XON 0x0100 + +#define I2_CRTSCTS (I2_OUTFLOW_CTS|I2_INFLOW_RTS) +#define I2_IXANY_MODE (I2_OUTFLOW_XON|I2_OUTFLOW_XANY) + +//------------------------------------------- +// Macros used from user level like functions +//------------------------------------------- + +// Macros to set and clear channel options +// +#define i2SetOption(pCh, option) pCh->channelOptions |= option +#define i2ClrOption(pCh, option) pCh->channelOptions &= ~option + +// Macro to set fatal-error trap +// +#define i2SetFatalTrap(pB, routine) pB->i2eFatalTrap = routine + +//-------------------------------------------- +// Declarations and prototypes for i2lib.c +//-------------------------------------------- +// +static int i2InitChannels(i2eBordStrPtr, int, i2ChanStrPtr); +static int i2QueueCommands(int, i2ChanStrPtr, int, int, cmdSyntaxPtr,...); +static int i2GetStatus(i2ChanStrPtr, int); +static int i2Input(i2ChanStrPtr); +static int i2InputFlush(i2ChanStrPtr); +static int i2Output(i2ChanStrPtr, const char *, int, int); +static int i2OutputFree(i2ChanStrPtr); +static int i2ServiceBoard(i2eBordStrPtr); +static void i2DrainOutput(i2ChanStrPtr, int); + +// Argument to i2QueueCommands +// +#define C_IN_LINE 1 +#define C_BYPASS 0 + +#endif // I2LIB_H diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2os.h linux/drivers/char/ip2/i2os.h --- v2.2.11/linux/drivers/char/ip2/i2os.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2os.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,145 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Defines, definitions and includes which are heavily dependant +* on O/S, host, compiler, etc. This file is tailored for: +* Linux v2.0.0 and later +* Gnu gcc c2.7.2 +* 80x86 architecture +* +*******************************************************************************/ + +#ifndef I2OS_H /* To prevent multiple includes */ +#define I2OS_H 1 + +#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) + +//------------------------------------------------- +// Required Includes +//------------------------------------------------- + +#include "ip2types.h" +#include /* For inb, etc */ + +//------------------------------------ +// Defines for I/O instructions: +//------------------------------------ + +#define INB(port) inb(port) +#define OUTB(port,value) outb((value),(port)) +#define INW(port) inw(port) +#define OUTW(port,value) outw((value),(port)) +#define OUTSW(port,addr,count) outsw((port),(addr),(((count)+1)/2)) +#define OUTSB(port,addr,count) outsb((port),(addr),(((count)+1))&-2) +#define INSW(port,addr,count) insw((port),(addr),(((count)+1)/2)) +#define INSB(port,addr,count) insb((port),(addr),(((count)+1))&-2) + +//-------------------------------------------- +// Interrupt control +//-------------------------------------------- + +#if LINUX_VERSION_CODE < 0x00020100 +typedef int spinlock_t; +#define spin_lock_init() +#define spin_lock(a) +#define spin_unlock(a) +#define spin_lock_irqsave(a,b) {save_flags((b));cli();} +#define spin_unlock_irqrestore(a,b) {restore_flags((b));} +#define write_lock_irqsave(a,b) spin_lock_irqsave(a,b) +#define write_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) +#define read_lock_irqsave(a,b) spin_lock_irqsave(a,b) +#define read_unlock_irqrestore(a,b) spin_unlock_irqrestore(a,b) +#endif + +//#define SAVE_AND_DISABLE_INTS(a,b) spin_lock_irqsave(a,b) +//#define RESTORE_INTS(a,b) spin_unlock_irqrestore(a,b) + +#define LOCK_INIT(a) spin_lock_init(a) + +#define SAVE_AND_DISABLE_INTS(a,b) { \ + /* printk("get_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + spin_lock_irqsave(a,b); \ +} + +#define RESTORE_INTS(a,b) { \ + /* printk("rel_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + spin_unlock_irqrestore(a,b); \ +} + +#define READ_LOCK_IRQSAVE(a,b) { \ + /* printk("get_read_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + read_lock_irqsave(a,b); \ +} + +#define READ_UNLOCK_IRQRESTORE(a,b) { \ + /* printk("rel_read_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + read_unlock_irqrestore(a,b); \ +} + +#define WRITE_LOCK_IRQSAVE(a,b) { \ + /* printk("get_write_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + write_lock_irqsave(a,b); \ +} + +#define WRITE_UNLOCK_IRQRESTORE(a,b) { \ + /* printk("rel_write_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ + write_unlock_irqrestore(a,b); \ +} + + +//------------------------------------------------------------------------------ +// Hardware-delay loop +// +// Probably used in only one place (see i2ellis.c) but this helps keep things +// together. Note we have unwound the IN instructions. On machines with a +// reasonable cache, the eight instructions (1 byte each) should fit in cache +// nicely, and on un-cached machines, the code-fetch would tend not to dominate. +// Note that cx is shifted so that "count" still reflects the total number of +// iterations assuming no unwinding. +//------------------------------------------------------------------------------ + +//#define DELAY1MS(port,count,label) + +//------------------------------------------------------------------------------ +// Macros to switch to a new stack, saving stack pointers, and to restore the +// old stack (Used, for example, in i2lib.c) "heap" is the address of some +// buffer which will become the new stack (working down from highest address). +// The two words at the two lowest addresses in this stack are for storing the +// SS and SP. +//------------------------------------------------------------------------------ + +//#define TO_NEW_STACK(heap,size) +//#define TO_OLD_STACK(heap) + +//------------------------------------------------------------------------------ +// Macros to save the original IRQ vectors and masks, and to patch in new ones. +//------------------------------------------------------------------------------ + +//#define SAVE_IRQ_MASKS(dest) +//#define WRITE_IRQ_MASKS(src) +//#define SAVE_IRQ_VECTOR(value,dest) +//#define WRITE_IRQ_VECTOR(value,src) + +//------------------------------------------------------------------------------ +// Macro to copy data from one far pointer to another. +//------------------------------------------------------------------------------ + +#define I2_MOVE_DATA(fpSource,fpDest,count) memmove(fpDest,fpSource,count); + +//------------------------------------------------------------------------------ +// Macros to issue eoi's to host interrupt control (IBM AT 8259-style). +//------------------------------------------------------------------------------ + +//#define MASTER_EOI +//#define SLAVE_EOI + +#endif /* I2OS_H */ + + diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/i2pack.h linux/drivers/char/ip2/i2pack.h --- v2.2.11/linux/drivers/char/ip2/i2pack.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/i2pack.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,364 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Definitions of the packets used to transfer data and commands +* Host <--> Board. Information provided here is only applicable +* when the standard loadware is active. +* +*******************************************************************************/ +#ifndef I2PACK_H +#define I2PACK_H 1 + +//----------------------------------------------- +// Revision History: +// +// 10 October 1991 MAG First draft +// 24 February 1992 MAG Additions for 1.4.x loadware +// 11 March 1992 MAG New status packets +// +//----------------------------------------------- + +//------------------------------------------------------------------------------ +// Packet Formats: +// +// Information passes between the host and board through the FIFO in packets. +// These have headers which indicate the type of packet. Because the fifo data +// path may be 16-bits wide, the protocol is constrained such that each packet +// is always padded to an even byte count. (The lower-level interface routines +// -- i2ellis.c -- are designed to do this). +// +// The sender (be it host or board) must place some number of complete packets +// in the fifo, then place a message in the mailbox that packets are available. +// Placing such a message interrupts the "receiver" (be it board or host), who +// reads the mailbox message and determines that there are incoming packets +// ready. Since there are no partial packets, and the length of a packet is +// given in the header, the remainder of the packet can be read without checking +// for FIFO empty condition. The process is repeated, packet by packet, until +// the incoming FIFO is empty. Then the receiver uses the outbound mailbox to +// signal the board that it has read the data. Only then can the sender place +// additional data in the fifo. +//------------------------------------------------------------------------------ +// +//------------------------------------------------ +// Definition of Packet Header Area +//------------------------------------------------ +// +// Caution: these only define header areas. In actual use the data runs off +// beyond the end of these structures. +// +// Since these structures are based on sequences of bytes which go to the board, +// there cannot be ANY padding between the elements. +#pragma pack(1) + +//---------------------------- +// DATA PACKETS +//---------------------------- + +typedef struct _i2DataHeader +{ + unsigned char i2sChannel; /* The channel number: 0-255 */ + + // -- Bitfields are allocated LSB first -- + + // For incoming data, indicates whether this is an ordinary packet or a + // special one (e.g., hot key hit). + unsigned i2sId : 2 __attribute__ ((__packed__)); + + // For tagging data packets. There are flush commands which flush only data + // packets bearing a particular tag. (used in implementing IntelliView and + // IntelliPrint). THE TAG VALUE 0xf is RESERVED and must not be used (it has + // meaning internally to the loadware). + unsigned i2sTag : 4; + + // These two bits determine the type of packet sent/received. + unsigned i2sType : 2; + + // The count of data to follow: does not include the possible additional + // padding byte. MAXIMUM COUNT: 4094. The top four bits must be 0. + unsigned short i2sCount; + +} i2DataHeader, *i2DataHeaderPtr; + +// Structure is immediately followed by the data, proper. + +//---------------------------- +// NON-DATA PACKETS +//---------------------------- + +typedef struct _i2CmdHeader +{ + unsigned char i2sChannel; // The channel number: 0-255 (Except where noted + // - see below + + // Number of bytes of commands, status or whatever to follow + unsigned i2sCount : 6; + + // These two bits determine the type of packet sent/received. + unsigned i2sType : 2; + +} i2CmdHeader, *i2CmdHeaderPtr; + +// Structure is immediately followed by the applicable data. + +//--------------------------------------- +// Flow Control Packets (Outbound) +//--------------------------------------- + +// One type of outbound command packet is so important that the entire structure +// is explicitly defined here. That is the flow-control packet. This is never +// sent by user-level code (as would be the commands to raise/lower DTR, for +// example). These are only sent by the library routines in response to reading +// incoming data into the buffers. +// +// The parameters inside the command block are maintained in place, then the +// block is sent at the appropriate time. + +typedef struct _flowIn +{ + i2CmdHeader hd; // Channel #, count, type (see above) + unsigned char fcmd; // The flow control command (37) + unsigned short asof; // As of byte number "asof" (LSB first!) I have room + // for "room" bytes + unsigned short room; +} flowIn, *flowInPtr; + +//---------------------------------------- +// (Incoming) Status Packets +//---------------------------------------- + +// Incoming packets which are non-data packets are status packets. In this case, +// the channel number in the header is unimportant. What follows are one or more +// sub-packets, the first word of which consists of the channel (first or low +// byte) and the status indicator (second or high byte), followed by possibly +// more data. + +#define STAT_CTS_UP 0 /* CTS raised (no other bytes) */ +#define STAT_CTS_DN 1 /* CTS dropped (no other bytes) */ +#define STAT_DCD_UP 2 /* DCD raised (no other bytes) */ +#define STAT_DCD_DN 3 /* DCD dropped (no other bytes) */ +#define STAT_DSR_UP 4 /* DSR raised (no other bytes) */ +#define STAT_DSR_DN 5 /* DSR dropped (no other bytes) */ +#define STAT_RI_UP 6 /* RI raised (no other bytes) */ +#define STAT_RI_DN 7 /* RI dropped (no other bytes) */ +#define STAT_BRK_DET 8 /* BRK detect (no other bytes) */ +#define STAT_FLOW 9 /* Flow control(-- more: see below */ +#define STAT_BMARK 10 /* Bookmark (no other bytes) + * Bookmark is sent as a response to + * a command 60: request for bookmark + */ +#define STAT_STATUS 11 /* Special packet: see below */ +#define STAT_TXCNT 12 /* Special packet: see below */ +#define STAT_RXCNT 13 /* Special packet: see below */ +#define STAT_BOXIDS 14 /* Special packet: see below */ +#define STAT_HWFAIL 15 /* Special packet: see below */ + +#define STAT_MOD_ERROR 0xc0 +#define STAT_MODEM 0xc0/* If status & STAT_MOD_ERROR: + * == STAT_MODEM, then this is a modem + * status packet, given in response to a + * CMD_DSS_NOW command. + * The low nibble has each data signal: + */ +#define STAT_MOD_DCD 0x8 +#define STAT_MOD_RI 0x4 +#define STAT_MOD_DSR 0x2 +#define STAT_MOD_CTS 0x1 + +#define STAT_ERROR 0x80/* If status & STAT_MOD_ERROR + * == STAT_ERROR, then + * sort of error on the channel. + * The remaining seven bits indicate + * what sort of error it is. + */ +/* The low three bits indicate parity, framing, or overrun errors */ + +#define STAT_E_PARITY 4 /* Parity error */ +#define STAT_E_FRAMING 2 /* Framing error */ +#define STAT_E_OVERRUN 1 /* (uxart) overrun error */ + +//--------------------------------------- +// STAT_FLOW packets +//--------------------------------------- + +typedef struct _flowStat +{ + unsigned short asof; + unsigned short room; +}flowStat, *flowStatPtr; + +// flowStat packets are received from the board to regulate the flow of outgoing +// data. A local copy of this structure is also kept to track the amount of +// credits used and credits remaining. "room" is the amount of space in the +// board's buffers, "as of" having received a certain byte number. When sending +// data to the fifo, you must calculate how much buffer space your packet will +// use. Add this to the current "asof" and subtract it from the current "room". +// +// The calculation for the board's buffer is given by CREDIT_USAGE, where size +// is the un-rounded count of either data characters or command characters. +// (Which is to say, the count rounded up, plus two). + +#define CREDIT_USAGE(size) (((size) + 3) & ~1) + +//--------------------------------------- +// STAT_STATUS packets +//--------------------------------------- + +typedef struct _debugStat +{ + unsigned char d_ccsr; + unsigned char d_txinh; + unsigned char d_stat1; + unsigned char d_stat2; +} debugStat, *debugStatPtr; + +// debugStat packets are sent to the host in response to a CMD_GET_STATUS +// command. Each byte is bit-mapped as described below: + +#define D_CCSR_XON 2 /* Has received XON, ready to transmit */ +#define D_CCSR_XOFF 4 /* Has received XOFF, not transmitting */ +#define D_CCSR_TXENAB 8 /* Transmitter is enabled */ +#define D_CCSR_RXENAB 0x80 /* Receiver is enabled */ + +#define D_TXINH_BREAK 1 /* We are sending a break */ +#define D_TXINH_EMPTY 2 /* No data to send */ +#define D_TXINH_SUSP 4 /* Output suspended via command 57 */ +#define D_TXINH_CMD 8 /* We are processing an in-line command */ +#define D_TXINH_LCD 0x10 /* LCD diagnostics are running */ +#define D_TXINH_PAUSE 0x20 /* We are processing a PAUSE command */ +#define D_TXINH_DCD 0x40 /* DCD is low, preventing transmission */ +#define D_TXINH_DSR 0x80 /* DSR is low, preventing transmission */ + +#define D_STAT1_TXEN 1 /* Transmit INTERRUPTS enabled */ +#define D_STAT1_RXEN 2 /* Receiver INTERRUPTS enabled */ +#define D_STAT1_MDEN 4 /* Modem (data set sigs) interrupts enabled */ +#define D_STAT1_RLM 8 /* Remote loopback mode selected */ +#define D_STAT1_LLM 0x10 /* Local internal loopback mode selected */ +#define D_STAT1_CTS 0x20 /* CTS is low, preventing transmission */ +#define D_STAT1_DTR 0x40 /* DTR is low, to stop remote transmission */ +#define D_STAT1_RTS 0x80 /* RTS is low, to stop remote transmission */ + +#define D_STAT2_TXMT 1 /* Transmit buffers are all empty */ +#define D_STAT2_RXMT 2 /* Receive buffers are all empty */ +#define D_STAT2_RXINH 4 /* Loadware has tried to inhibit remote + * transmission: dropped DTR, sent XOFF, + * whatever... + */ +#define D_STAT2_RXFLO 8 /* Loadware can send no more data to host + * until it receives a flow-control packet + */ +//----------------------------------------- +// STAT_TXCNT and STAT_RXCNT packets +//---------------------------------------- + +typedef struct _cntStat +{ + unsigned short cs_time; // (Assumes host is little-endian!) + unsigned short cs_count; +} cntStat, *cntStatPtr; + +// These packets are sent in response to a CMD_GET_RXCNT or a CMD_GET_TXCNT +// bypass command. cs_time is a running 1 Millisecond counter which acts as a +// time stamp. cs_count is a running counter of data sent or received from the +// uxarts. (Not including data added by the chip itself, as with CRLF +// processing). +//------------------------------------------ +// STAT_HWFAIL packets +//------------------------------------------ + +typedef struct _failStat +{ + unsigned char fs_written; + unsigned char fs_read; + unsigned short fs_address; +} failStat, *failStatPtr; + +// This packet is sent whenever the on-board diagnostic process detects an +// error. At startup, this process is dormant. The host can wake it up by +// issuing the bypass command CMD_HW_TEST. The process runs at low priority and +// performs continuous hardware verification; writing data to certain on-board +// registers, reading it back, and comparing. If it detects an error, this +// packet is sent to the host, and the process goes dormant again until the host +// sends another CMD_HW_TEST. It then continues with the next register to be +// tested. + +//------------------------------------------------------------------------------ +// Macros to deal with the headers more easily! Note that these are defined so +// they may be used as "left" as well as "right" expressions. +//------------------------------------------------------------------------------ + +// Given a pointer to the packet, reference the channel number +// +#define CHANNEL_OF(pP) ((i2DataHeaderPtr)(pP))->i2sChannel + +// Given a pointer to the packet, reference the Packet type +// +#define PTYPE_OF(pP) ((i2DataHeaderPtr)(pP))->i2sType + +// The possible types of packets +// +#define PTYPE_DATA 0 /* Host <--> Board */ +#define PTYPE_BYPASS 1 /* Host ---> Board */ +#define PTYPE_INLINE 2 /* Host ---> Board */ +#define PTYPE_STATUS 2 /* Host <--- Board */ + +// Given a pointer to a Data packet, reference the Tag +// +#define TAG_OF(pP) ((i2DataHeaderPtr)(pP))->i2sTag + +// Given a pointer to a Data packet, reference the data i.d. +// +#define ID_OF(pP) ((i2DataHeaderPtr)(pP))->i2sId + +// The possible types of ID's +// +#define ID_ORDINARY_DATA 0 +#define ID_HOT_KEY 1 + +// Given a pointer to a Data packet, reference the count +// +#define DATA_COUNT_OF(pP) ((i2DataHeaderPtr)(pP))->i2sCount + +// Given a pointer to a Data packet, reference the beginning of data +// +#define DATA_OF(pP) &((unsigned char *)(pP))[4] // 4 = size of header + +// Given a pointer to a Non-Data packet, reference the count +// +#define CMD_COUNT_OF(pP) ((i2CmdHeaderPtr)(pP))->i2sCount + +#define MAX_CMD_PACK_SIZE 62 // Maximum size of such a count + +// Given a pointer to a Non-Data packet, reference the beginning of data +// +#define CMD_OF(pP) &((unsigned char *)(pP))[2] // 2 = size of header + +//-------------------------------- +// MailBox Bits: +//-------------------------------- + +//-------------------------- +// Outgoing (host to board) +//-------------------------- +// +#define MB_OUT_STUFFED 0x80 // Host has placed output in fifo +#define MB_IN_STRIPPED 0x40 // Host has read in all input from fifo + +//-------------------------- +// Incoming (board to host) +//-------------------------- +// +#define MB_IN_STUFFED 0x80 // Board has placed input in fifo +#define MB_OUT_STRIPPED 0x40 // Board has read all output from fifo +#define MB_FATAL_ERROR 0x20 // Board has encountered a fatal error + +#pragma pack(4) // Reset padding to command-line default + +#endif // I2PACK_H + diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/ip2.h linux/drivers/char/ip2/ip2.h --- v2.2.11/linux/drivers/char/ip2/ip2.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/ip2.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,109 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants for configuration and tuning +* +* NOTES: +* +*******************************************************************************/ +#ifndef IP2_H +#define IP2_H + +#include "ip2types.h" +#include "i2cmd.h" + +/*************/ +/* Constants */ +/*************/ + +/* Device major numbers + * The first set are the major numbers allocated from the Linux Device Registry. + * This was expanded from 64 to 128 with version 2.0.26. If this code is built + * under earlier versions we use majors from the LOCAL/EXPERIMENTAL range. + */ +#if MAX_CHRDEV > 64 +# define IP2_TTY_MAJOR 71 +# define IP2_CALLOUT_MAJOR 72 +# define IP2_IPL_MAJOR 73 +#else +# define IP2_TTY_MAJOR 60 +# define IP2_CALLOUT_MAJOR 61 +# define IP2_IPL_MAJOR 62 +#endif + + +/* Board configuration array. + * This array defines the hardware irq and address for up to IP2_MAX_BOARDS + * (4 supported per ip2_types.h) ISA board addresses and irqs MUST be specified, + * PCI and EISA boards are probed for and automagicly configed + * iff the addresses are set to 1 and 2 respectivily. + * 0x0100 - 0x03f0 == ISA + * 1 == PCI + * 2 == EISA + * 0 == (skip this board) + * This array defines the hardware addresses for them. Special + * addresses are EISA and PCI which go sniffing for boards. + + * In a multiboard system the position in the array determines which port + * devices are assigned to each board: + * board 0 is assigned ttyF0.. to ttyF63, + * board 1 is assigned ttyF64 to ttyF127, + * board 2 is assigned ttyF128 to ttyF191, + * board 3 is assigned ttyF192 to ttyF255. + * + * In PCI and EISA bus systems each range is mapped to card in + * monotonically increasing slot number order, ISA position is as specified + * here. + + * If the irqs are ALL set to 0,0,0,0 all boards operate in + * polled mode. For interrupt operation ISA boards require that the IRQ be + * specified, while PCI and EISA boards any nonzero entry + * will enable interrupts using the BIOS configured irq for the board. + * An invalid irq entry will default to polled mode for that card and print + * console warning. + + * When the driver is loaded as a module these setting can be overridden on the + * modprobe command line or on an option line in /etc/conf.modules + * or /etc/modules.conf depending on your distrubution. + * If the driver is built-in the configuration must be + * set here for ISA cards and address set to 1 and 2 for PCI and EISA. + * + * Here is an example that shows most if not all possibe combinations: + + *static ip2config_t ip2config = + *{ + * {11,1,0,0}, // irqs + * { // Addresses + * 0x0308, // Board 0, ttyF0 - ttyF63// ISA card at io=0x308, irq=11 + * 0x0001, // Board 1, ttyF64 - ttyF127//PCI card configured by BIOS + * 0x0000, // Board 2, ttyF128 - ttyF191// Slot skipped + * 0x0002 // Board 3, ttyF192 - ttyF255//EISA card configured by BIOS + * // but polled not irq driven + * } + *}; + */ + + /* this structure is zeroed out because the suggested method is to configure + * the driver as a module, set up the parameters with an options line in + * /etc/modules.conf or /etc/conf.modules and load with modprobe, kerneld or + * kmod, the kernel module loader + */ +static ip2config_t ip2config = +{ + {0,0,0,0}, // irqs + { // Addresses + 0x0000, // Board 0, ttyF0 - ttyF63 + 0x0000, // Board 1, ttyF64 - ttyF127 + 0x0000, // Board 2, ttyF128 - ttyF191 + 0x0000 // Board 3, ttyF192 - ttyF255 + } +}; + +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/ip2ioctl.h linux/drivers/char/ip2/ip2ioctl.h --- v2.2.11/linux/drivers/char/ip2/ip2ioctl.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/ip2ioctl.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,35 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants for configuration and tuning +* +* NOTES: +* +*******************************************************************************/ + +#ifndef IP2IOCTL_H +#define IP2IOCTL_H + +//************* +//* Constants * +//************* + +// High baud rates (if not defined elsewhere. +#ifndef B153600 +# define B153600 0010005 +#endif +#ifndef B307200 +# define B307200 0010006 +#endif +#ifndef B921600 +# define B921600 0010007 +#endif + +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/ip2trace.h linux/drivers/char/ip2/ip2trace.h --- v2.2.11/linux/drivers/char/ip2/ip2trace.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/ip2trace.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,43 @@ + +// +union ip2breadcrumb +{ + struct { + unsigned char port, cat, codes, label; + } __attribute__ ((packed)) hdr; + unsigned long value; +}; + +#define ITRC_NO_PORT 0xFF +#define PORTN (port->port_index) +#define CHANN (pCh->port_index) + +#define ITRC_ERROR '!' +#define ITRC_INIT 'A' +#define ITRC_OPEN 'B' +#define ITRC_CLOSE 'C' +#define ITRC_DRAIN 'D' +#define ITRC_IOCTL 'E' +#define ITRC_FLUSH 'F' +#define ITRC_STATUS 'G' +#define ITRC_HANGUP 'H' +#define ITRC_INTR 'I' +#define ITRC_SFLOW 'J' +#define ITRC_SBCMD 'K' +#define ITRC_SICMD 'L' +#define ITRC_MODEM 'M' +#define ITRC_INPUT 'N' +#define ITRC_OUTPUT 'O' +#define ITRC_PUTC 'P' +#define ITRC_QUEUE 'Q' +#define ITRC_STFLW 'R' +#define ITRC_SFIFO 'S' +#define ITRC_VERIFY 'V' +#define ITRC_WRITE 'W' + +#define ITRC_ENTER 0x00 +#define ITRC_RETURN 0xFF + +#define ITRC_QUEUE_ROOM 2 +#define ITRC_QUEUE_CMD 6 + diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2/ip2types.h linux/drivers/char/ip2/ip2types.h --- v2.2.11/linux/drivers/char/ip2/ip2types.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2/ip2types.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,54 @@ +/******************************************************************************* +* +* (c) 1998 by Computone Corporation +* +******************************************************************************** +* +* +* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Driver constants and type definitions. +* +* NOTES: +* +*******************************************************************************/ +#ifndef IP2TYPES_H +#define IP2TYPES_H + +//************* +//* Constants * +//************* + +// Define some limits for this driver. Ports per board is a hardware limitation +// that will not change. Current hardware limits this to 64 ports per board. +// Boards per driver is a self-imposed limit. +// +#define IP2_MAX_BOARDS 4 +#define IP2_PORTS_PER_BOARD ABS_MOST_PORTS +#define IP2_MAX_PORTS (IP2_MAX_BOARDS*IP2_PORTS_PER_BOARD) + +#define ISA 0 +#define PCI 1 +#define EISA 2 + +//******************** +//* Type Definitions * +//******************** + +typedef struct tty_struct * PTTY; +typedef struct wait_queue * PWAITQ; + +typedef unsigned char UCHAR; +typedef unsigned int UINT; +typedef unsigned short USHORT; +typedef unsigned long ULONG; + +typedef struct +{ + short irq[IP2_MAX_BOARDS]; + unsigned short addr[IP2_MAX_BOARDS]; + int type[IP2_MAX_BOARDS]; +} ip2config_t; + +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2.c linux/drivers/char/ip2.c --- v2.2.11/linux/drivers/char/ip2.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2.c Wed Aug 25 17:29:47 1999 @@ -0,0 +1,73 @@ +// ip2.c +// This is a dummy module to make the firmware available when needed +// and allows it to be unloaded when not. Rumor is the __initdata +// macro doesn't always works on all platforms so we use this kludge. +// If not compiled as a module it just makes fip_firm avaliable then +// __initdata should work as advertized +// + +#include +#include +#include +#include +#include + +#include "./ip2/ip2types.h" +#include "./ip2/fip_firm.h" // the meat + +int +ip2_loadmain(int *, int *, unsigned char *, int ); // ref into ip2main.c + +#ifdef MODULE +static int io[IP2_MAX_BOARDS]= { 0,}; +static int irq[IP2_MAX_BOARDS] = { 0,}; + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +MODULE_PARM(irq,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); +MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards"); +MODULE_PARM(io,"1-"__MODULE_STRING(IP2_MAX_BOARDS) "i"); +MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards"); + + +//====================================================================== +int +init_module(void) +{ + int rc; + + MOD_INC_USE_COUNT; // hold till done + + rc = ip2_loadmain(io,irq,(unsigned char *)fip_firm,sizeof(fip_firm)); + // The call to lock and load main, create dep + + MOD_DEC_USE_COUNT; //done - kerneld now can unload us + return rc; +} + +//====================================================================== +int +ip2_init(void) +{ + // call to this is int tty_io.c so we need this + return 0; +} + +//====================================================================== +void +cleanup_module(void) +{ +} + +#else // !MODULE + +#ifndef NULL +# define NULL ((void *) 0) +#endif + +int +ip2_init(void) { + return ip2_loadmain(NULL,NULL,(unsigned char *)fip_firm,sizeof(fip_firm)); +} + +#endif /* !MODULE */ diff -u --recursive --new-file v2.2.11/linux/drivers/char/ip2main.c linux/drivers/char/ip2main.c --- v2.2.11/linux/drivers/char/ip2main.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ip2main.c Wed Aug 25 17:29:47 1999 @@ -0,0 +1,3236 @@ +/******************************************************************************* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +/************/ +/* Includes */ +/************/ + +#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 +#include +#include + +#include +#include +#include + +#include +#define pcibios_strerror(status) \ + printk( KERN_ERR "IP2: PCI error 0x%x \n", status ); + +#include "./ip2/ip2trace.h" +#include "./ip2/ip2ioctl.h" +#include "./ip2/ip2.h" +#include "./ip2/i2ellis.h" +#include "./ip2/i2lib.h" + +/***************** + * /proc/ip2mem * + *****************/ + +#include + +int ip2_read_procmem(char *, char **, off_t, int, int ); +int ip2_read_proc(char *, char **, off_t, int, int *, void * ); + +struct proc_dir_entry ip2_proc_entry = { + 0, + 6,"ip2mem", + S_IFREG | S_IRUGO, + 1, 0, 0, + 0, + NULL, + ip2_read_procmem +}; + +/********************/ +/* Type Definitions */ +/********************/ + +/*************/ +/* Constants */ +/*************/ + +/* String constants to identify ourselves */ +static char *pcName = "Computone IntelliPort Plus multiport driver"; +static char *pcVersion = "1.2.4"; + +/* String constants for port names */ +static char *pcDriver_name = "ip2"; +static char *pcTty = "ttyf"; +static char *pcCallout = "cuf"; +static char *pcIpl = "ip2ipl"; + +/* Serial subtype definitions */ +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +// cheezy kludge or genius - you decide? +int ip2_loadmain(int *, int *, unsigned char *, int); +static unsigned char *Fip_firmware; +static int Fip_firmware_size; + +/***********************/ +/* Function Prototypes */ +/***********************/ + +/* Global module entry functions */ +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif + +int old_ip2_init(void); + +/* Private (static) functions */ +static int ip2_open(PTTY, struct file *); +static void ip2_close(PTTY, struct file *); +static int ip2_write(PTTY, int, const unsigned char *, int); +static void ip2_putchar(PTTY, unsigned char); +static void ip2_flush_chars(PTTY); +static int ip2_write_room(PTTY); +static int ip2_chars_in_buf(PTTY); +static void ip2_flush_buffer(PTTY); +static int ip2_ioctl(PTTY, struct file *, UINT, ULONG); +static void ip2_set_termios(PTTY, struct termios *); +static void ip2_set_line_discipline(PTTY); +static void ip2_throttle(PTTY); +static void ip2_unthrottle(PTTY); +static void ip2_stop(PTTY); +static void ip2_start(PTTY); +static void ip2_hangup(PTTY); + +static void set_irq(int, int); +static void ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static void ip2_poll(unsigned long arg); +static inline void service_all_boards(void); +static inline void do_input(i2ChanStrPtr pCh); +static inline void do_status(i2ChanStrPtr pCh); + +static void ip2_wait_until_sent(PTTY,int); + +static void set_params (i2ChanStrPtr, struct termios *); +static int get_modem_info(i2ChanStrPtr, unsigned int *); +static int set_modem_info(i2ChanStrPtr, unsigned int, unsigned int *); +static int get_serial_info(i2ChanStrPtr, struct serial_struct *); +static int set_serial_info(i2ChanStrPtr, struct serial_struct *); + +static ssize_t ip2_ipl_read(struct file *, char *, size_t, loff_t *) ; +static ssize_t ip2_ipl_write(struct file *, const char *, size_t, loff_t *); +static int ip2_ipl_ioctl(struct inode *, struct file *, UINT, ULONG); +static int ip2_ipl_open(struct inode *, struct file *); + +void ip2trace(unsigned short,unsigned char,unsigned char,unsigned long,...); +static int DumpTraceBuffer(char *, int); +static int DumpFifoBuffer( char *, int); + +static void ip2_init_board(int); +static unsigned short find_eisa_board(int); + +/***************/ +/* Static Data */ +/***************/ + +static struct tty_driver ip2_tty_driver; +static struct tty_driver ip2_callout_driver; + +static int ref_count; + +/* Here, then is a table of board pointers which the interrupt routine should + * scan through to determine who it must service. + */ +static unsigned short i2nBoards = 0; // Number of boards here + +static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS]; + +static i2ChanStrPtr DevTable[IP2_MAX_PORTS]; +//DevTableMem just used to save addresses for kfree +static void *DevTableMem[IP2_MAX_BOARDS] = {NULL,NULL,NULL,NULL}; + +static struct tty_struct * TtyTable[IP2_MAX_PORTS]; +static struct termios * Termios[IP2_MAX_PORTS]; +static struct termios * TermiosLocked[IP2_MAX_PORTS]; + +/* This is the driver descriptor for the ip2ipl device, which is used to + * download the loadware to the boards. + */ +static struct file_operations +ip2_ipl = { + NULL, + ip2_ipl_read, + ip2_ipl_write, + NULL, + NULL, + ip2_ipl_ioctl, + NULL, + ip2_ipl_open, + NULL, + NULL, + NULL, + NULL, + NULL, + /* NULL, NULL 2.2 */ +}; + +static long irq_counter = 0; +static long bh_counter = 0; + +// Use immediate queue to service interrupts +//#define USE_IQI // PCI&2.2 needs work +//#define USE_IQ // PCI&2.2 needs work + +/* The timer_list entry for our poll routine. If interrupt operation is not + * selected, the board is serviced periodically to see if anything needs doing. + */ +#define POLL_TIMEOUT (jiffies + 1) +static struct timer_list PollTimer = { NULL, NULL, 0, 0, ip2_poll }; +// next, prev, expires,data, func() +static char TimerOn = 0; + +#ifdef IP2DEBUG_TRACE +/* Trace (debug) buffer data */ +#define TRACEMAX 1000 +static unsigned long tracebuf[TRACEMAX]; +static int tracestuff = 0; +static int tracestrip = 0; +static int tracewrap = 0; +#endif + +/**********/ +/* Macros */ +/**********/ + +#if defined(MODULE) && defined(IP2DEBUG_OPEN) +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \ + kdevname(tty->device),(pCh->flags),ref_count, \ + tty->count,/*GET_USE_COUNT(module)*/0,s) +#else +#define DBG_CNT(s) +#endif + +#define MIN(a,b) ( ( (a) < (b) ) ? (a) : (b) ) +#define MAX(a,b) ( ( (a) > (b) ) ? (a) : (b) ) + +/********/ +/* Code */ +/********/ + +#include "./ip2/i2ellis.c" /* Extremely low-level interface services */ +#include "./ip2/i2cmd.c" /* Standard loadware command definitions */ +#include "./ip2/i2lib.c" /* High level interface services */ + +/* Configuration area for modprobe */ +#ifdef MODULE +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +#endif /* MODULE */ + +static int poll_only = 0; + +static int Pci_index = 0; +static int Eisa_irq = 0; +static int Eisa_slot = 0; + +static int iindx = 0; +static char rirqs[IP2_MAX_BOARDS] = {0,}; +static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; + +/******************************************************************************/ +/* Initialisation Section */ +/******************************************************************************/ +int +ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) +{ + int i; + /* process command line arguments to modprobe or insmod i.e. iop & irqp */ + /* otherwise ip2config is initialized by what's in ip2/ip2.h */ + /* command line trumps initialization in ip2.h */ + /* first two args are null if builtin to kernel */ + if ((irqp != NULL) || (iop != NULL)) { + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (irqp && irqp[i]) { + ip2config.irq[i] = irqp[i]; + } + if (iop && iop[i]) { + ip2config.addr[i] = iop[i]; + } + } + } + Fip_firmware = firmware; + Fip_firmware_size = firmsize; + return old_ip2_init(); +} + +// Some functions to keep track of what irq's we have + +__initfunc(static int +is_valid_irq(int irq) ) +{ + int *i = Valid_Irqs; + + while ((*i != 0) && (*i != irq)) { + i++; + } + return (*i); +} + +__initfunc( static void +mark_requested_irq( char irq )) +{ + rirqs[iindx++] = irq; +} + +__initfunc( static int +clear_requested_irq( char irq )) +{ + int i; + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (rirqs[i] == irq) { + rirqs[i] = 0; + return 1; + } + } + return 0; +} + +__initfunc( static int +have_requested_irq( char irq )) +{ + // array init to zeros so 0 irq will not be requested as a side effect + int i; + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (rirqs[i] == irq) + return 1; + } + return 0; +} + +/******************************************************************************/ +/* Function: init_module() */ +/* Parameters: None */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It simply calls */ +/* the driver initialisation function and returns what it returns. */ +/******************************************************************************/ +#ifdef MODULE +int +init_module(void) +{ +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "Loading module ...\n" ); +#endif + //was return old_ip2_init(); + return 0; +} +#endif /* MODULE */ + +/******************************************************************************/ +/* Function: cleanup_module() */ +/* Parameters: None */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It has to return */ +/* the device and the driver to a passive state. It should not be necessary */ +/* to reset the board fully, especially as the loadware is downloaded */ +/* externally rather than in the driver. We just want to disable the board */ +/* and clear the loadware to a reset state. To allow this there has to be a */ +/* way to detect whether the board has the loadware running at init time to */ +/* handle subsequent installations of the driver. All memory allocated by the */ +/* driver should be returned since it may be unloaded from memory. */ +/******************************************************************************/ +#ifdef MODULE +void +cleanup_module(void) +{ + int err; + int i; + +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "Unloading %s: version %s\n", pcName, pcVersion ); +#endif + + + /* Stop poll timer if we had one. */ + if ( TimerOn ) { + del_timer ( &PollTimer ); + TimerOn = 0; + } + + /* Reset the boards we have. */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + iiReset ( i2BoardPtrTable[i] ); + } + } + + /* The following is done at most once, if any boards were installed. */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + iiResetDelay( i2BoardPtrTable[i] ); + /* free io addresses and Tibet */ + release_region( ip2config.addr[i], 8 ); + } + /* Disable and remove interrupt handler. */ + if ( (ip2config.irq[i] > 0) && have_requested_irq(ip2config.irq[i]) ) { + free_irq ( ip2config.irq[i], (void *)&pcName); + clear_requested_irq( ip2config.irq[i]); + } + } + if ( ( err = tty_unregister_driver ( &ip2_tty_driver ) ) ) { + printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err); + } + if ( ( err = tty_unregister_driver ( &ip2_callout_driver ) ) ) { + printk(KERN_ERR "IP2: failed to unregister callout driver (%d)\n", err); + } + if ( ( err = unregister_chrdev ( IP2_IPL_MAJOR, pcIpl ) ) ) { + printk(KERN_ERR "IP2: failed to unregister IPL driver (%d)\n", err); + } + if ( ( err = proc_unregister( &proc_root, ip2_proc_entry.low_ino ) ) ) { + printk(KERN_ERR "IP2: failed to unregister read_procmem (%d)\n", err); + } + + // free memory + for (i = 0; i < IP2_MAX_BOARDS; i++) { + void *pB; + if ((pB = i2BoardPtrTable[i]) != 0 ) { + kfree ( pB ); + i2BoardPtrTable[i] = NULL; + } + if ((DevTableMem[i]) != NULL ) { + kfree ( DevTableMem[i] ); + DevTableMem[i] = NULL; + } + } + + /* Cleanup the iiEllis subsystem. */ + iiEllisCleanup(); +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "IP2 Unloaded\n" ); +#endif +} +#endif /* MODULE */ + +/******************************************************************************/ +/* Function: old_ip2_init() */ +/* Parameters: irq, io from command line of insmod et. al. */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This was the required entry point for all drivers (now in ip2.c) */ +/* It performs all */ +/* initialisation of the devices and driver structures, and registers itself */ +/* with the relevant kernel modules. */ +/******************************************************************************/ +/* SA_INTERRUPT- if set blocks all interrupts else only this line */ +/* SA_SHIRQ - for shared irq PCI or maybe EISA only */ +/* SA_RANDOM - can be source for cert. random number generators */ +#define IP2_SA_FLAGS 0 + +__initfunc( int +old_ip2_init(void)) +{ + int i; + int err; + int status = 0; + i2eBordStrPtr pB = NULL; + int rc = -1; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 ); +#endif + + /* Announce our presence */ + printk( KERN_INFO "%s version %s\n", pcName, pcVersion ); + + /* if all irq config is zero we shall poll_only */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + poll_only |= ip2config.irq[i]; + } + poll_only = !poll_only; + + /* Initialise the iiEllis subsystem. */ + iiEllisInit(); + + /* Initialize arrays. */ + memset( i2BoardPtrTable, 0, sizeof i2BoardPtrTable ); + memset( DevTable, 0, sizeof DevTable ); + memset( TtyTable, 0, sizeof TtyTable ); + memset( Termios, 0, sizeof Termios ); + memset( TermiosLocked, 0, sizeof TermiosLocked ); + + /* Initialise all the boards we can find (up to the maximum). */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + switch ( ip2config.addr[i] ) { + case 0: /* skip this slot even if card is present */ + break; + default: /* ISA */ + /* ISA address must be specified */ + if ( (ip2config.addr[i] < 0x100) || (ip2config.addr[i] > 0x3f8) ) { + printk ( KERN_ERR "IP2: Bad ISA board %d address %x\n", + i, ip2config.addr[i] ); + ip2config.addr[i] = 0; + } else { + ip2config.type[i] = ISA; + + /* Check for valid irq argument, set for polling if invalid */ + if (ip2config.irq[i] && !is_valid_irq(ip2config.irq[i])) { + printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",ip2config.irq[i]); + ip2config.irq[i] = 0;// 0 is polling and is valid in that sense + } + } + break; + case PCI: +#ifdef CONFIG_PCI + if (pci_present()) { + struct pci_dev *pci_dev_i = NULL; + pci_dev_i = pci_find_device(PCI_VENDOR_ID_COMPUTONE, + PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i); + if (pci_dev_i != NULL) { + unsigned int addr; + unsigned char pci_irq; + + ip2config.type[i] = PCI; + /* + * Update Pci_index, so that the next time we go + * searching for a PCI board we find a different + * one. + */ + ++Pci_index; + status = + pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr); + if ( addr & 1 ) { + ip2config.addr[i]=(USHORT)(addr&0xfffe); + } else { + printk( KERN_ERR "IP2: PCI I/O address error\n"); + } + status = + pci_read_config_byte(pci_dev_i, PCI_INTERRUPT_LINE, &pci_irq); + + if (!is_valid_irq(pci_irq)) { + printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq); + pci_irq = 0; + } + ip2config.irq[i] = pci_irq; + } else { // ann error + ip2config.addr[i] = 0; + if (status == PCIBIOS_DEVICE_NOT_FOUND) { + printk( KERN_ERR "IP2: PCI board %d not found\n", i ); + } else { + pcibios_strerror(status); + } + } + } +#else + printk( KERN_ERR "IP2: PCI card specified but PCI support not\n"); + printk( KERN_ERR "IP2: configured in this kernel.\n"); + printk( KERN_ERR "IP2: Recompile kernel with CONFIG_PCI defined!\n"); +#endif /* CONFIG_PCI */ + break; + case EISA: + if ( (ip2config.addr[i] = find_eisa_board( Eisa_slot + 1 )) != 0) { + /* Eisa_irq set as side effect, boo */ + ip2config.type[i] = EISA; + } + ip2config.irq[i] = Eisa_irq; + break; + } /* switch */ + } /* for */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( ip2config.addr[i] ) { + pB = kmalloc( sizeof(i2eBordStr), GFP_KERNEL); + if ( pB != NULL ) { + i2BoardPtrTable[i] = pB; + memset( pB, 0, sizeof(i2eBordStr) ); + iiSetAddress( pB, ip2config.addr[i], ii2DelayTimer ); + iiReset( pB ); + } else { + printk(KERN_ERR "IP2: board memory allocation error\n"); + } + } + } + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( ( pB = i2BoardPtrTable[i] ) != NULL ) { + iiResetDelay( pB ); + break; + } + } + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] != NULL ) { + ip2_init_board( i ); + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 2, 0 ); +#endif + + /* Zero out the normal tty device structure. */ + memset ( &ip2_tty_driver, 0, sizeof ip2_tty_driver ); + + /* Initialise the relevant fields. */ + ip2_tty_driver.magic = TTY_DRIVER_MAGIC; + ip2_tty_driver.name = pcTty; + ip2_tty_driver.driver_name = pcDriver_name; + ip2_tty_driver.read_proc = ip2_read_proc; + ip2_tty_driver.major = IP2_TTY_MAJOR; + ip2_tty_driver.minor_start = 0; + ip2_tty_driver.num = IP2_MAX_PORTS; + ip2_tty_driver.type = TTY_DRIVER_TYPE_SERIAL; + ip2_tty_driver.subtype = SERIAL_TYPE_NORMAL; + ip2_tty_driver.init_termios = tty_std_termios; + ip2_tty_driver.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + ip2_tty_driver.flags = TTY_DRIVER_REAL_RAW; + ip2_tty_driver.refcount = &ref_count; + ip2_tty_driver.table = TtyTable; + ip2_tty_driver.termios = Termios; + ip2_tty_driver.termios_locked = TermiosLocked; + + /* Setup the pointers to the implemented functions. */ + ip2_tty_driver.open = ip2_open; + ip2_tty_driver.close = ip2_close; + ip2_tty_driver.write = ip2_write; + ip2_tty_driver.put_char = ip2_putchar; + ip2_tty_driver.flush_chars = ip2_flush_chars; + ip2_tty_driver.write_room = ip2_write_room; + ip2_tty_driver.chars_in_buffer = ip2_chars_in_buf; + ip2_tty_driver.flush_buffer = ip2_flush_buffer; + ip2_tty_driver.ioctl = ip2_ioctl; + ip2_tty_driver.throttle = ip2_throttle; + ip2_tty_driver.unthrottle = ip2_unthrottle; + ip2_tty_driver.set_termios = ip2_set_termios; + ip2_tty_driver.set_ldisc = ip2_set_line_discipline; + ip2_tty_driver.stop = ip2_stop; + ip2_tty_driver.start = ip2_start; + ip2_tty_driver.hangup = ip2_hangup; + + /* Initialise the callout driver structure from the tty driver, and + * make the needed adjustments. + */ + ip2_callout_driver = ip2_tty_driver; + ip2_callout_driver.name = pcCallout; + ip2_callout_driver.driver_name = pcDriver_name; + ip2_callout_driver.read_proc = NULL; + ip2_callout_driver.major = IP2_CALLOUT_MAJOR; + ip2_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 3, 0 ); +#endif + + /* Register the tty devices. */ + if ( ( err = tty_register_driver ( &ip2_tty_driver ) ) ) { + printk(KERN_ERR "IP2: failed to register tty driver (%d)\n", err); + } else + if ( ( err = tty_register_driver ( &ip2_callout_driver ) ) ) { + printk(KERN_ERR "IP2: failed to register callout driver (%d)\n", err); + } else + /* Register the IPL driver. */ + if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) ) { + printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err ); + } else + /* Register the read_procmem thing */ + if ( ( err = proc_register( &proc_root, &ip2_proc_entry ) ) ) { + printk(KERN_ERR "IP2: failed to register read_procmem (%d)\n", err ); + } else { + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, 4, 0 ); +#endif + /* Register the interrupt handler or poll handler, depending upon the + * specified interrupt. + */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( 0 == ip2config.addr[i] ) { + continue; + } + if (poll_only) { + ip2config.irq[i] = CIR_POLL; + } + if ( ip2config.irq[i] == CIR_POLL ) { +retry: + if (!TimerOn) { + PollTimer.expires = POLL_TIMEOUT; + add_timer ( &PollTimer ); + TimerOn = 1; + printk( KERN_INFO "IP2: polling\n"); + } + } else { + if (have_requested_irq(ip2config.irq[i])) + continue; + rc = request_irq( ip2config.irq[i], ip2_interrupt, + IP2_SA_FLAGS | (ip2config.type[i] == PCI ? SA_SHIRQ : 0), + pcName, (void *)&pcName); + if (rc) { + printk(KERN_ERR "IP2: an request_irq failed: error %d\n",rc); + ip2config.irq[i] = CIR_POLL; + printk( KERN_INFO "IP2: Polling %ld/sec.\n", + (POLL_TIMEOUT - jiffies)); + goto retry; + } + mark_requested_irq(ip2config.irq[i]); + /* Initialise the interrupt handler bottom half (aka slih). */ + } + } + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */ + } + } + } +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 ); +#endif + + return 0; +} + +/******************************************************************************/ +/* Function: ip2_init_board() */ +/* Parameters: Index of board in configuration structure */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This function initializes the specified board. The loadware is copied to */ +/* the board, the channel structures are initialized, and the board details */ +/* are reported on the console. */ +/******************************************************************************/ +__initfunc( static void +ip2_init_board( int boardnum )) +{ + int i,rc; + int nports = 0, nboxes = 0; + i2ChanStrPtr pCh; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + + if ( !iiInitialize ( pB ) ) { + printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n", + pB->i2eBase, pB->i2eError ); + kfree ( pB ); + i2BoardPtrTable[boardnum] = NULL; + return; + } + printk(KERN_INFO "Board %d: addr=0x%x irq=%d ", boardnum + 1, + ip2config.addr[boardnum], ip2config.irq[boardnum] ); + + if (0 != ( rc = check_region( ip2config.addr[boardnum], 8))) { + i2BoardPtrTable[boardnum] = NULL; + printk(KERN_ERR "bad addr=0x%x rc = %d\n", + ip2config.addr[boardnum], rc ); + return; + } + request_region( ip2config.addr[boardnum], 8, pcName ); + + if ( iiDownloadAll ( pB, (loadHdrStrPtr)Fip_firmware, 1, Fip_firmware_size ) + != II_DOWN_GOOD ) { + printk ( KERN_ERR "IP2:failed to download loadware " ); + } else { + printk ( KERN_INFO "fv=%d.%d.%d lv=%d.%d.%d\n", + pB->i2ePom.e.porVersion, + pB->i2ePom.e.porRevision, + pB->i2ePom.e.porSubRev, pB->i2eLVersion, + pB->i2eLRevision, pB->i2eLSub ); + } + + switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) { + + default: + printk( KERN_ERR "IP2: Unknown board type, ID = %x", + pB->i2ePom.e.porID ); + nports = 0; + goto ex_exit; + break; + + case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */ + printk ( KERN_INFO "ISA-4" ); + nports = 4; + break; + + case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */ + printk ( KERN_INFO "ISA-8 std" ); + nports = 8; + break; + + case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */ + printk ( KERN_INFO "ISA-8 RJ11" ); + nports = 8; + break; + + case POR_ID_FIIEX: /* IntelliPort IIEX */ + { + int portnum = IP2_PORTS_PER_BOARD * boardnum; + int box; + + for( box = 0; box < ABS_MAX_BOXES; ++box ) { + if ( pB->i2eChannelMap[box] != 0 ) { + ++nboxes; + } + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & 1<< i ) { + ++nports; + } + } + } + DevTableMem[boardnum] = pCh = + kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL ); + if ( !i2InitChannels( pB, nports, pCh ) ) { + printk(KERN_ERR "i2InitChannels failed: %d\n",pB->i2eError); + } + pB->i2eChannelPtr = &DevTable[portnum]; + pB->i2eChannelCnt = ABS_MOST_PORTS; + + for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) { + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & (1 << i) ) { + DevTable[portnum + i] = pCh; + pCh->port_index = portnum + i; + pCh++; + } + } + } + printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit", + nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 ); + } + goto ex_exit; + break; + } + DevTableMem[boardnum] = pCh = + kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL ); + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nports; + i2InitChannels ( pB, pB->i2eChannelCnt, pCh ); + pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum]; + + for( i = 0; i < pB->i2eChannelCnt; ++i ) { + DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh; + pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i; + pCh++; + } +ex_exit: + printk ( KERN_INFO "\n" ); +} + +/******************************************************************************/ +/* Function: find_eisa_board ( int start_slot ) */ +/* Parameters: First slot to check */ +/* Returns: Address of EISA IntelliPort II controller */ +/* */ +/* Description: */ +/* This function searches for an EISA IntelliPort controller, starting */ +/* from the specified slot number. If the motherboard is not identified as an */ +/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */ +/* it returns the base address of the controller. */ +/******************************************************************************/ +__initfunc( static unsigned short +find_eisa_board( int start_slot )) +{ + int i, j; + unsigned int idm = 0; + unsigned int idp = 0; + unsigned int base = 0; + unsigned int value; + int setup_address; + int setup_irq; + int ismine = 0; + + /* + * First a check for an EISA motherboard, which we do by comparing the + * EISA ID registers for the system board and the first couple of slots. + * No slot ID should match the system board ID, but on an ISA or PCI + * machine the odds are that an empty bus will return similar values for + * each slot. + */ + i = 0x0c80; + value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3); + for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) { + j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3); + if ( value == j ) + return 0; + } + + /* + * OK, so we are inclined to believe that this is an EISA machine. Find + * an IntelliPort controller. + */ + for( i = start_slot; i < 16; i++ ) { + base = i << 12; + idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff); + idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff); + ismine = 0; + if ( idm == 0x0e8e ) { + if ( idp == 0x0281 || idp == 0x0218 ) { + ismine = 1; + } else if ( idp == 0x0282 || idp == 0x0283 ) { + ismine = 3; /* Can do edge-trigger */ + } + if ( ismine ) { + Eisa_slot = i; + break; + } + } + } + if ( !ismine ) + return 0; + + /* It's some sort of EISA card, but at what address is it configured? */ + + setup_address = base + 0xc88; + value = inb(base + 0xc86); + setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0; + + if ( (ismine & 2) && !(value & 0x10) ) { + ismine = 1; /* Could be edging, but not */ + } + + if ( Eisa_irq == 0 ) { + Eisa_irq = setup_irq; + } else if ( Eisa_irq != setup_irq ) { + printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" ); + } + +#ifdef IP2DEBUG_INIT +printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x", + base >> 12, idm, idp, setup_address); + if ( Eisa_irq ) { + printk(KERN_DEBUG ", Interrupt %d %s\n", + setup_irq, (ismine & 2) ? "(edge)" : "(level)"); + } else { + printk(KERN_DEBUG ", (polled)\n"); + } +#endif + return setup_address; +} + +/******************************************************************************/ +/* Function: set_irq() */ +/* Parameters: index to board in board table */ +/* IRQ to use */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/******************************************************************************/ +static void +set_irq( int boardnum, int boardIrq ) +{ + i2ChanStrPtr pCh; + unsigned char tempCommand[16]; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + unsigned long flags; + + /* + * Notify the boards they may generate interrupts. This is done by + * sending an in-line command to channel 0 on each board. This is why + * the channels have to be defined already. For each board, if the + * interrupt has never been defined, we must do so NOW, directly, since + * board will not send flow control or even give an interrupt until this + * is done. If polling we must send 0 as the interrupt parameter. + */ + + pCh = (i2ChanStrPtr) pB->i2eChannelPtr; + + // We will get an interrupt here at the end of this function + + iiDisableMailIrq(pB); + + /* We build up the entire packet header. */ + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_INLINE; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = CMDVALUE_IRQ; + (CMD_OF(tempCommand))[1] = boardIrq; + /* + * Write to FIFO; don't bother to adjust fifo capacity for this, since + * board will respond almost immediately after SendMail hit. + */ + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 4); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + pB->i2eUsingIrq = boardIrq; + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + + /* Need to update number of boards before you enable mailbox int */ + ++i2nBoards; + + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 6; + (CMD_OF(tempCommand))[0] = 88; // SILO + (CMD_OF(tempCommand))[1] = 64; // chars + (CMD_OF(tempCommand))[2] = 32; // ms + + (CMD_OF(tempCommand))[3] = 28; // MAX_BLOCK + (CMD_OF(tempCommand))[4] = 64; // chars + + (CMD_OF(tempCommand))[5] = 87; // HW_TEST + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 8); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 1; + (CMD_OF(tempCommand))[0] = 84; /* get BOX_IDS */ + iiWriteBuf(pB, tempCommand, 3); + +#ifdef XXX + // enable heartbeat for test porpoises + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_BYPASS; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = 44; /* get ping */ + (CMD_OF(tempCommand))[1] = 200; /* 200 ms */ + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 4); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); +#endif + + iiEnableMailIrq(pB); + iiSendPendingMail(pB); +} + +/******************************************************************************/ +/* Interrupt Handler Section */ +/******************************************************************************/ + +static inline void +service_all_boards() +{ + int i; + i2eBordStrPtr pB; + + /* Service every board on the list */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB ) { + i2ServiceBoard( pB ); + } + } +} + + +#ifdef USE_IQI +static struct tq_struct +senior_service = +{ // it's the death that worse than fate + NULL, + 0, + (void(*)(void*)) service_all_boards, + NULL, //later - board address XXX +}; +#endif + +/******************************************************************************/ +/* Function: ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs) */ +/* Parameters: irq - interrupt number */ +/* pointer to optional device ID structure */ +/* pointer to register structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + int i; + i2eBordStrPtr pB; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, irq ); +#endif + +#ifdef USE_IQI + + queue_task(&senior_service, &tq_immediate); + mark_bh(IMMEDIATE_BH); + +#else + /* Service just the boards on the list using this irq */ + for( i = 0; i < i2nBoards; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB && (pB->i2eUsingIrq == irq) ) { + i2ServiceBoard( pB ); + } + } + +#endif /* USE_IQI */ + + ++irq_counter; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_poll(unsigned long arg) */ +/* Parameters: ? */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function calls the library routine i2ServiceBoard for each board in */ +/* the board table. This is used instead of the interrupt routine when polled */ +/* mode is specified. */ +/******************************************************************************/ +static void +ip2_poll(unsigned long arg) +{ +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 ); +#endif + TimerOn = 0; // it's the truth but not checked in service + + bh_counter++; + +#ifdef USE_IQI + + queue_task(&senior_service, &tq_immediate); + mark_bh(IMMEDIATE_BH); + +#else + // Just polled boards, service_all might be better + ip2_interrupt(0, NULL, NULL); + +#endif /* USE_IQI */ + + PollTimer.expires = POLL_TIMEOUT; + add_timer( &PollTimer ); + TimerOn = 1; + +#ifdef IP2DEBUG_TRACE + ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); +#endif +} + +static inline void +do_input( i2ChanStrPtr pCh ) +{ + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace(PORTN, ITRC_INPUT, 21, 0 ); +#endif + // Data input + if ( pCh->pTTY != NULL ) { + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags) + if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) { + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + i2Input( pCh ); + } else + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) + } else { +#ifdef IP2DEBUG_TRACE + ip2trace(PORTN, ITRC_INPUT, 22, 0 ); +#endif + i2InputFlush( pCh ); + } +} + +// code duplicated from n_tty (ldisc) +static inline void +isig(int sig, struct tty_struct *tty, int flush) +{ + if (tty->pgrp > 0) + kill_pg(tty->pgrp, sig, 1); + if (flush || !L_NOFLSH(tty)) { + if ( tty->ldisc.flush_buffer ) + tty->ldisc.flush_buffer(tty); + i2InputFlush( tty->driver_data ); + } +} + +static inline void +do_status( i2ChanStrPtr pCh ) +{ + int status; + + status = i2GetStatus( pCh, (I2_BRK|I2_PAR|I2_FRA|I2_OVR) ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_STATUS, 21, 1, status ); +#endif + + if (pCh->pTTY && (status & (I2_BRK|I2_PAR|I2_FRA|I2_OVR)) ) { + if ( (status & I2_BRK) ) { + // code duplicated from n_tty (ldisc) + if (I_IGNBRK(pCh->pTTY)) + goto skip_this; + if (I_BRKINT(pCh->pTTY)) { + isig(SIGINT, pCh->pTTY, 1); + goto skip_this; + } + wake_up_interruptible(&pCh->pTTY->read_wait); + } +#ifdef NEVER_HAPPENS_AS_SETUP_XXX + // and can't work because we don't know the_char + // as the_char is reported on a seperate path + // The intelligent board does this stuff as setup + { + char brkf = TTY_NORMAL; + unsigned char brkc = '\0'; + unsigned char tmp; + if ( (status & I2_BRK) ) { + brkf = TTY_BREAK; + brkc = '\0'; + } + else if (status & I2_PAR) { + brkf = TTY_PARITY; + brkc = the_char; + } else if (status & I2_FRA) { + brkf = TTY_FRAME; + brkc = the_char; + } else if (status & I2_OVR) { + brkf = TTY_OVERRUN; + brkc = the_char; + } + tmp = pCh->pTTY->real_raw; + pCh->pTTY->real_raw = 0; + pCh->pTTY->ldisc.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); + pCh->pTTY->real_raw = tmp; + } +#endif /* NEVER_HAPPENS_AS_SETUP_XXX */ + } +skip_this: + + if ( status & (I2_DDCD | I2_DDSR | I2_DCTS | I2_DRI) ) { + wake_up_interruptible(&pCh->delta_msr_wait); + + if ( (pCh->flags & ASYNC_CHECK_CD) && (status & I2_DDCD) ) { + if ( status & I2_DCD ) { + if ( pCh->wopen ) { + wake_up_interruptible ( &pCh->open_wait ); + } + } else if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) ) { + if (pCh->pTTY && (!(pCh->pTTY->termios->c_cflag & CLOCAL)) ) { + tty_hangup( pCh->pTTY ); + } + } + } + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_STATUS, 26, 0 ); +#endif +} + +/******************************************************************************/ +/* Device Open/Close/Ioctl Entry Point Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: open_sanity_check() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* Verifies the structure magic numbers and cross links. */ +/******************************************************************************/ +#ifdef IP2DEBUG_OPEN +static void +open_sanity_check( i2ChanStrPtr pCh, i2eBordStrPtr pBrd ) +{ + if ( pBrd->i2eValid != I2E_MAGIC ) { + printk(KERN_ERR "IP2: invalid board structure\n" ); + } else if ( pBrd != pCh->pMyBord ) { + printk(KERN_ERR "IP2: board structure pointer mismatch (%p)\n", + pCh->pMyBord ); + } else if ( pBrd->i2eChannelCnt < pCh->port_index ) { + printk(KERN_ERR "IP2: bad device index (%d)\n", pCh->port_index ); + } else if (&((i2ChanStrPtr)pBrd->i2eChannelPtr)[pCh->port_index] != pCh) { + } else { + printk(KERN_INFO "IP2: all pointers check out!\n" ); + } +} +#endif + + +/******************************************************************************/ +/* Function: ip2_open() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: (MANDATORY) */ +/* A successful device open has to run a gauntlet of checks before it */ +/* completes. After some sanity checking and pointer setup, the function */ +/* blocks until all conditions are satisfied. It then initialises the port to */ +/* the default characteristics and returns. */ +/******************************************************************************/ +static int +ip2_open( PTTY tty, struct file *pFile ) +{ + int rc = 0; + int do_clocal = 0; + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + +#ifdef IP2DEBUG_TRACE + ip2trace (MINOR(tty->device), ITRC_OPEN, ITRC_ENTER, 0 ); +#endif + + if ( pCh == NULL ) { + return -ENODEV; + } + /* Setup pointer links in device and tty structures */ + pCh->pTTY = tty; + tty->driver_data = pCh; + MOD_INC_USE_COUNT; + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG \ + "IP2:open(tty=%p,pFile=%p):dev=%x,maj=%d,min=%d,ch=%d,idx=%d\n", + tty, pFile, tty->device, MAJOR(tty->device), MINOR(tty->device), + pCh->infl.hd.i2sChannel, pCh->port_index); + open_sanity_check ( pCh, pCh->pMyBord ); +#endif + + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, + CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP); + serviceOutgoingFifo( pCh->pMyBord ); + + /* Block here until the port is ready (per serial and istallion) */ + /* + * 1. If the port is in the middle of closing wait for the completion + * and then return the appropriate error. + */ + if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) { + if ( pCh->flags & ASYNC_CLOSING ) { + interruptible_sleep_on( &pCh->close_wait); + } + if ( tty_hung_up_p(pFile) ) { + return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS; + } + } + /* + * 2. If this is a callout device, make sure the normal port is not in + * use, and that someone else doesn't have the callout device locked. + * (These are the only tests the standard serial driver makes for + * callout devices.) + */ + if ( tty->driver.subtype == SERIAL_TYPE_CALLOUT ) { + if ( pCh->flags & ASYNC_NORMAL_ACTIVE ) { + return -EBUSY; + } + if ( ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) && + ( pCh->flags & ASYNC_SESSION_LOCKOUT ) && + ( pCh->session != current->session ) ) { + return -EBUSY; + } + if ( ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) && + ( pCh->flags & ASYNC_PGRP_LOCKOUT ) && + ( pCh->pgrp != current->pgrp ) ) { + return -EBUSY; + } + pCh->flags |= ASYNC_CALLOUT_ACTIVE; + goto noblock; + } + /* + * 3. Handle a non-blocking open of a normal port. + */ + if ( (pFile->f_flags & O_NONBLOCK) || (tty->flags & (1<flags & ASYNC_CALLOUT_ACTIVE ) { + return -EBUSY; + } + pCh->flags |= ASYNC_NORMAL_ACTIVE; + goto noblock; + } + /* + * 4. Now loop waiting for the port to be free and carrier present + * (if required). + */ + if ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) { + if ( pCh->NormalTermios.c_cflag & CLOCAL ) { + do_clocal = 1; + } + } else { + if ( tty->termios->c_cflag & CLOCAL ) { + do_clocal = 1; + } + } + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "OpenBlock: do_clocal = %d\n", do_clocal); +#endif + + ++pCh->wopen; + for(;;) { + if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE)) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + serviceOutgoingFifo( pCh->pMyBord ); + } + if ( tty_hung_up_p(pFile) ) { + return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS; + } + if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) && + !(pCh->flags & ASYNC_CLOSING) && + (do_clocal || (pCh->dataSetIn & I2_DCD) )) { + rc = 0; + break; + } + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "ASYNC_CALLOUT_ACTIVE = %s\n", + (pCh->flags & ASYNC_CALLOUT_ACTIVE)?"True":"False"); + printk(KERN_DEBUG "ASYNC_CLOSING = %s\n", + (pCh->flags & ASYNC_CLOSING)?"True":"False"); + printk(KERN_DEBUG "OpenBlock: waiting for CD or signal\n"); +#endif +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, 3, 2, (pCh->flags & ASYNC_CALLOUT_ACTIVE), + (pCh->flags & ASYNC_CLOSING) ); +#endif + /* check for signal */ + if (signal_pending(current)) { + rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS); + break; + } + interruptible_sleep_on(&pCh->open_wait); + } + --pCh->wopen; //why count? +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, 4, 0 ); +#endif + if (rc != 0 ) { + return rc; + } + pCh->flags |= ASYNC_NORMAL_ACTIVE; + +noblock: + + /* first open - Assign termios structure to port */ + if ( tty->count == 1 ) { + i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + if ( pCh->flags & ASYNC_SPLIT_TERMIOS ) { + if ( tty->driver.subtype == SERIAL_TYPE_NORMAL ) { + *tty->termios = pCh->NormalTermios; + } else { + *tty->termios = pCh->CalloutTermios; + } + } + /* Now we must send the termios settings to the loadware */ + set_params( pCh, NULL ); + } + + /* override previous and never reset ??? */ + pCh->session = current->session; + pCh->pgrp = current->pgrp; + + /* + * Now set any i2lib options. These may go away if the i2lib code ends + * up rolled into the mainline. + */ + pCh->channelOptions |= CO_NBLOCK_WRITE; + +#ifdef IP2DEBUG_OPEN + printk (KERN_DEBUG "IP2: open completed\n" ); +#endif + serviceOutgoingFifo( pCh->pMyBord ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_OPEN, ITRC_RETURN, 0 ); +#endif + return 0; +} + +/******************************************************************************/ +/* Function: ip2_close() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_close( PTTY tty, struct file *pFile ) +{ + i2ChanStrPtr pCh = tty->driver_data; + + if ( !pCh ) { + return; + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, ITRC_ENTER, 0 ); +#endif + +#ifdef IP2DEBUG_OPEN + printk(KERN_DEBUG "IP2:close ttyF%02X:\n",MINOR(tty->device)); +#endif + + if ( tty_hung_up_p ( pFile ) ) { + MOD_DEC_USE_COUNT; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, 2, 1, 2 ); +#endif + return; + } + if ( tty->count > 1 ) { /* not the last close */ + MOD_DEC_USE_COUNT; +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, 2, 1, 3 ); +#endif + return; + } + pCh->flags |= ASYNC_CLOSING; // last close actually + + /* + * Save the termios structure, since this port may have separate termios + * for callout and dialin. + */ + if (pCh->flags & ASYNC_NORMAL_ACTIVE) + pCh->NormalTermios = *tty->termios; + if (pCh->flags & ASYNC_CALLOUT_ACTIVE) + pCh->CalloutTermios = *tty->termios; + + tty->closing = 1; + + if (pCh->ClosingWaitTime != ASYNC_CLOSING_WAIT_NONE) { + /* + * Before we drop DTR, make sure the transmitter has completely drained. + * This uses an timeout, after which the close + * completes. + */ + ip2_wait_until_sent(tty, pCh->ClosingWaitTime ); + } + /* + * At this point we stop accepting input. Here we flush the channel + * input buffer which will allow the board to send up more data. Any + * additional input is tossed at interrupt/poll time. + */ + i2InputFlush( pCh ); + + /* disable DSS reporting */ + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP); + if ( !tty || (tty->termios->c_cflag & HUPCL) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, + CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + + serviceOutgoingFifo ( pCh->pMyBord ); + + if ( tty->driver.flush_buffer ) + tty->driver.flush_buffer(tty); + if ( tty->ldisc.flush_buffer ) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + + pCh->pTTY = NULL; + + if (pCh->wopen) { + if (pCh->ClosingDelay) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; + schedule_timeout(pCh->ClosingDelay); + } + wake_up_interruptible(&pCh->open_wait); + } + + pCh->flags &=~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&pCh->close_wait); + +#ifdef IP2DEBUG_OPEN + DBG_CNT("ip2_close: after wakeups--"); +#endif + + MOD_DEC_USE_COUNT; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_CLOSE, ITRC_RETURN, 1, 1 ); +#endif + return; +} + +/******************************************************************************/ +/* Function: ip2_hangup() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_hangup ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_HANGUP, ITRC_ENTER, 0 ); +#endif + + ip2_flush_buffer(tty); + + /* disable DSS reporting */ + + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP); + i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + if ( !tty || (tty->termios->c_cflag & HUPCL) ) { + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + } + i2QueueCommands(PTYPE_INLINE, pCh, 1, 3, + CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP); + serviceOutgoingFifo ( pCh->pMyBord ); + + wake_up_interruptible ( &pCh->delta_msr_wait ); + + pCh->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + pCh->pTTY = NULL; + wake_up_interruptible ( &pCh->open_wait ); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_HANGUP, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/******************************************************************************/ +/* Device Output Section */ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_write() */ +/* Parameters: Pointer to tty structure */ +/* Flag denoting data is in user (1) or kernel (0) space */ +/* Pointer to data */ +/* Number of bytes to write */ +/* Returns: Number of bytes actually written */ +/* */ +/* Description: (MANDATORY) */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_write( PTTY tty, int user, const unsigned char *pData, int count) +{ + i2ChanStrPtr pCh = tty->driver_data; + int bytesSent = 0; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, ITRC_ENTER, 2, count, -1 ); +#endif + + /* Flush out any buffered data left over from ip2_putchar() calls. */ + ip2_flush_chars( tty ); + + /* This is the actual move bit. Make sure it does what we need!!!!! */ + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + bytesSent = i2Output( pCh, pData, count, user ); + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent ); +#endif + return bytesSent > 0 ? bytesSent : 0; +} + +/******************************************************************************/ +/* Function: ip2_putchar() */ +/* Parameters: Pointer to tty structure */ +/* Character to write */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_putchar( PTTY tty, unsigned char ch ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, ITRC_ENTER, 1, ch ); +#endif + + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + pCh->Pbuf[pCh->Pbuf_stuff++] = ch; + if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) { + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + ip2_flush_chars( tty ); + } else + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, ITRC_RETURN, 1, ch ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_flush_chars() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/******************************************************************************/ +static void +ip2_flush_chars( PTTY tty ) +{ + int strip; + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + if ( pCh->Pbuf_stuff ) { +#ifdef IP2DEBUG_TRACE +// ip2trace (PORTN, ITRC_PUTC, 10, 1, strip ); +#endif + // + // We may need to restart i2Output if it does not fullfill this request + // + strip = i2Output( pCh, pCh->Pbuf, pCh->Pbuf_stuff, 0 ); + if ( strip != pCh->Pbuf_stuff ) { + memmove( pCh->Pbuf, &pCh->Pbuf[strip], pCh->Pbuf_stuff - strip ); + } + pCh->Pbuf_stuff -= strip; + } + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); +} + +/******************************************************************************/ +/* Function: ip2_write_room() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Number of bytes that the driver can accept */ +/* */ +/* Description: */ +/* */ +/******************************************************************************/ +static int +ip2_write_room ( PTTY tty ) +{ + int bytesFree; + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + + READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff; + READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, 11, 1, bytesFree ); +#endif + + return ((bytesFree > 0) ? bytesFree : 0); +} + +/******************************************************************************/ +/* Function: ip2_chars_in_buf() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Number of bytes queued for transmission */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_chars_in_buf ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + int rc; + unsigned long flags; +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_WRITE, 12, 1, pCh->Obuf_char_count + pCh->Pbuf_stuff ); +#endif +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: chars in buffer = %d (%d,%d)\n", + pCh->Obuf_char_count + pCh->Pbuf_stuff, + pCh->Obuf_char_count, pCh->Pbuf_stuff ); +#endif + READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags); + rc = pCh->Obuf_char_count; + READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags); + READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + rc += pCh->Pbuf_stuff; + READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + return rc; +} + +/******************************************************************************/ +/* Function: ip2_flush_buffer() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_flush_buffer( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_FLUSH, ITRC_ENTER, 0 ); +#endif +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: flush buffer\n" ); +#endif + WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags); + pCh->Pbuf_stuff = 0; + WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags); + i2FlushOutput( pCh ); + ip2_owake(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_FLUSH, ITRC_RETURN, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: ip2_wait_until_sent() */ +/* Parameters: Pointer to tty structure */ +/* Timeout for wait. */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function is used in place of the normal tty_wait_until_sent, which */ +/* only waits for the driver buffers to be empty (or rather, those buffers */ +/* reported by chars_in_buffer) which doesn't work for IP2 due to the */ +/* indeterminate number of bytes buffered on the board. */ +/******************************************************************************/ +static void +ip2_wait_until_sent ( PTTY tty, int timeout ) +{ + int i = jiffies; + i2ChanStrPtr pCh = tty->driver_data; + + tty_wait_until_sent(tty, timeout ); + if ( (i = timeout - (jiffies -i)) > 0) + i2DrainOutput( pCh, i ); +} + +/******************************************************************************/ +/******************************************************************************/ +/* Device Input Section */ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_throttle() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_throttle ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "IP2: throttle\n" ); +#endif + /* + * Signal the poll/interrupt handlers not to forward incoming data to + * the line discipline. This will cause the buffers to fill up in the + * library and thus cause the library routines to send the flow control + * stuff. + */ + pCh->throttled = 1; +} + +/******************************************************************************/ +/* Function: ip2_unthrottle() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_unthrottle ( PTTY tty ) +{ + i2ChanStrPtr pCh = tty->driver_data; + unsigned long flags; + +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "IP2: unthrottle\n" ); +#endif + + /* Pass incoming data up to the line discipline again. */ + pCh->throttled = 0; + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); + serviceOutgoingFifo( pCh->pMyBord ); + READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags) + if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) { + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) +#ifdef IP2DEBUG_READ + printk (KERN_DEBUG "i2Input called from unthrottle\n" ); +#endif + i2Input( pCh ); + } else + READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags) +} + +static void +ip2_start ( PTTY tty ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + + i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_UNSUSPEND); + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_RESUME); +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: start tx\n" ); +#endif +} + +static void +ip2_stop ( PTTY tty ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_SUSPEND); +#ifdef IP2DEBUG_WRITE + printk (KERN_DEBUG "IP2: stop tx\n" ); +#endif +} + +/******************************************************************************/ +/* Device Ioctl Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_ioctl() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to file structure */ +/* Command */ +/* Argument */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) +{ + i2ChanStrPtr pCh = DevTable[MINOR(tty->device)]; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + int rc = 0; + + if ( pCh == NULL ) { + return -ENODEV; + } + +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, ITRC_ENTER, 2, cmd, arg ); +#endif + +#ifdef IP2DEBUG_IOCTL + printk(KERN_DEBUG "IP2: ioctl cmd (%x), arg (%lx)\n", cmd, arg ); +#endif + + switch(cmd) { + case TIOCGSERIAL: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 2, 1, rc ); +#endif + rc = get_serial_info(pCh, (struct serial_struct *) arg); + if (rc) + return rc; + break; + + case TIOCSSERIAL: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 3, 1, rc ); +#endif + rc = set_serial_info(pCh, (struct serial_struct *) arg); + if (rc) + return rc; + break; + + case TCXONC: + rc = tty_check_change(tty); + if (rc) + return rc; + switch (arg) { + case TCOOFF: + //return -ENOIOCTLCMD; + break; + case TCOON: + //return -ENOIOCTLCMD; + break; + case TCIOFF: + if (STOP_CHAR(tty) != __DISABLED_CHAR) { + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, + CMD_XMIT_NOW(STOP_CHAR(tty))); + } + break; + case TCION: + if (START_CHAR(tty) != __DISABLED_CHAR) { + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1, + CMD_XMIT_NOW(START_CHAR(tty))); + } + break; + default: + return -EINVAL; + } + return 0; + + case TCSBRK: /* SVID version: non-zero arg --> no break */ + rc = tty_check_change(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 4, 1, rc ); +#endif + if (!rc) { + ip2_wait_until_sent(tty,0); + if (!arg) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SEND_BRK(250)); + serviceOutgoingFifo( pCh->pMyBord ); + } + } + break; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + rc = tty_check_change(tty); +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 5, 1, rc ); +#endif + if (!rc) { + ip2_wait_until_sent(tty,0); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_SEND_BRK(arg ? arg*100 : 250)); + serviceOutgoingFifo ( pCh->pMyBord ); + } + break; + + case TIOCGSOFTCAR: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 6, 1, rc ); +#endif + rc=put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + if (rc) + return rc; + break; + + case TIOCSSOFTCAR: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 7, 1, rc ); +#endif + rc=get_user(arg,(unsigned long *) arg); + if (rc) + return rc; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) + | (arg ? CLOCAL : 0)); + + break; + + case TIOCMGET: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 8, 1, rc ); +#endif + rc = get_modem_info(pCh, (unsigned int *) arg); + if (rc) + return rc; + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 9, 0 ); +#endif + rc = set_modem_info(pCh, cmd, (unsigned int *) arg); + break; + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - mask + * passed in arg for lines of interest (use |'ed TIOCM_RNG/DSR/CD/CTS + * for masking). Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + cprev = pCh->icount; /* note the counters on entry */ + for(;;) { +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 10, 0 ); +#endif + interruptible_sleep_on(&pCh->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + cnow = pCh->icount; /* atomic copy */ + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + rc = -EIO; /* no change => rc */ + break; + } + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + rc = 0; + break; + } + cprev = cnow; + } + /* NOTREACHED */ + break; + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for RI where + * only 0->1 is counted. The controller is quite capable of counting + * both, but this done to preserve compatibility with the standard + * serial driver. + */ + case TIOCGICOUNT: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 11, 1, rc ); +#endif + cnow = pCh->icount; + p_cuser = (struct serial_icounter_struct *) arg; + put_user(cnow.cts, &p_cuser->cts); + put_user(cnow.dsr, &p_cuser->dsr); + put_user(cnow.rng, &p_cuser->rng); + put_user(cnow.dcd, &p_cuser->dcd); + break; + + /* + * The rest are not supported by this driver. By returning -ENOIOCTLCMD they + * will be passed to the line discipline for it to handle. + */ + case TIOCSERCONFIG: + case TIOCSERGWILD: + case TIOCSERGETLSR: + case TIOCSERSWILD: + case TIOCSERGSTRUCT: + case TIOCSERGETMULTI: + case TIOCSERSETMULTI: + + default: +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, 12, 0 ); +#endif + rc = -ENOIOCTLCMD; + break; + } +#ifdef IP2DEBUG_TRACE + ip2trace (PORTN, ITRC_IOCTL, ITRC_RETURN, 0 ); +#endif + return rc; +} +/******************************************************************************/ +/* Function: get_modem_info() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to destination for data */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This returns the current settings of the dataset signal inputs to the user */ +/* program. */ +/******************************************************************************/ +static int +get_modem_info(i2ChanStrPtr pCh, unsigned int *value) +{ + unsigned short status; + unsigned int result; + int rc; + + status = pCh->dataSetIn; // snapshot settings + result = ((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0) + | ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0) + | ((status & I2_DCD) ? TIOCM_CAR : 0) + | ((status & I2_RI) ? TIOCM_RNG : 0) + | ((status & I2_DSR) ? TIOCM_DSR : 0) + | ((status & I2_CTS) ? TIOCM_CTS : 0); + rc=put_user(result,value); + return rc; +} + +/******************************************************************************/ +/* Function: set_modem_info() */ +/* Parameters: Pointer to channel structure */ +/* Specific ioctl command */ +/* Pointer to source for new settings */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This returns the current settings of the dataset signal inputs to the user */ +/* program. */ +/******************************************************************************/ +static int +set_modem_info(i2ChanStrPtr pCh, unsigned cmd, unsigned int *value) +{ + int rc; + unsigned int arg; + + rc=get_user(arg,value); + if (rc) + return rc; + switch(cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP); + pCh->dataSetOut |= I2_RTS; + } + if (arg & TIOCM_DTR) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP); + pCh->dataSetOut |= I2_DTR; + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN); + pCh->dataSetOut &= ~I2_RTS; + } + if (arg & TIOCM_DTR) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN); + pCh->dataSetOut &= ~I2_DTR; + } + break; + case TIOCMSET: + if ( (arg & TIOCM_RTS) && !(pCh->dataSetOut & I2_RTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP); + pCh->dataSetOut |= I2_RTS; + } else if ( !(arg & TIOCM_RTS) && (pCh->dataSetOut & I2_RTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN); + pCh->dataSetOut &= ~I2_RTS; + } + if ( (arg & TIOCM_DTR) && !(pCh->dataSetOut & I2_DTR) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP); + pCh->dataSetOut |= I2_DTR; + } else if ( !(arg & TIOCM_DTR) && (pCh->dataSetOut & I2_DTR) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN); + pCh->dataSetOut &= ~I2_DTR; + } + break; + default: + return -EINVAL; + } + serviceOutgoingFifo( pCh->pMyBord ); + return 0; +} + +/******************************************************************************/ +/* Function: GetSerialInfo() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is to support the setserial command, and requires processing of the */ +/* standard Linux serial structure. */ +/******************************************************************************/ +static int +get_serial_info ( i2ChanStrPtr pCh, struct serial_struct *retinfo ) +{ + struct serial_struct tmp; + int rc=0; + + if ( !retinfo ) { + return -EFAULT; + } + + memset ( &tmp, 0, sizeof(tmp) ); + tmp.type = pCh->pMyBord->channelBtypes.bid_value[(pCh->port_index & (IP2_PORTS_PER_BOARD-1))/16]; + if (BID_HAS_654(tmp.type)) { + tmp.type = PORT_16650; + } else { + tmp.type = PORT_CIRRUS; + } + tmp.line = pCh->port_index; + tmp.port = pCh->pMyBord->i2eBase; + tmp.irq = ip2config.irq[pCh->port_index/64]; + tmp.flags = pCh->flags; + tmp.baud_base = pCh->BaudBase; + tmp.close_delay = pCh->ClosingDelay; + tmp.closing_wait = pCh->ClosingWaitTime; + tmp.custom_divisor = pCh->BaudDivisor; + if(copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + rc= -EFAULT; + return rc; +} + +/******************************************************************************/ +/* Function: SetSerialInfo() */ +/* Parameters: Pointer to channel structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This function provides support for setserial, which uses the TIOCSSERIAL */ +/* ioctl. Not all setserial parameters are relevant. If the user attempts to */ +/* change the IRQ, address or type of the port the ioctl fails. */ +/******************************************************************************/ +static int +set_serial_info( i2ChanStrPtr pCh, struct serial_struct *new_info ) +{ + struct serial_struct ns; + int old_flags, old_baud_divisor; + int rc = 0; + + if ( !new_info ) { + return -EFAULT; + } + rc=copy_from_user(&ns, new_info, sizeof (ns) ); + if (rc) { + return rc; + } + /* + * We don't allow setserial to change IRQ, board address, type or baud + * base. Also line nunber as such is meaningless but we use it for our + * array index so it is fixed also. + */ + if ( ns.irq != ip2config.irq + || (int) ns.port != ((int) pCh->pMyBord->i2eBase) + || ns.baud_base != pCh->BaudBase + || ns.line != pCh->port_index ) { + return -EINVAL; + } + + old_flags = pCh->flags; + old_baud_divisor = pCh->BaudDivisor; + + if ( !suser() ) { + if ( ( ns.close_delay != pCh->ClosingDelay ) || + ( (ns.flags & ~ASYNC_USR_MASK) != + (pCh->flags & ~ASYNC_USR_MASK) ) ) { + return -EPERM; + } + + pCh->flags = (pCh->flags & ~ASYNC_USR_MASK) | + (ns.flags & ASYNC_USR_MASK); + pCh->BaudDivisor = ns.custom_divisor; + } else { + pCh->flags = (pCh->flags & ~ASYNC_FLAGS) | + (ns.flags & ASYNC_FLAGS); + pCh->BaudDivisor = ns.custom_divisor; + pCh->ClosingDelay = ns.close_delay * HZ/100; + pCh->ClosingWaitTime = ns.closing_wait * HZ/100; + } + + if ( ( (old_flags & ASYNC_SPD_MASK) != (pCh->flags & ASYNC_SPD_MASK) ) + || (old_baud_divisor != pCh->BaudDivisor) ) { + // Invalidate speed and reset parameters + set_params( pCh, NULL ); + } + + return rc; +} + +/******************************************************************************/ +/* Function: ip2_set_termios() */ +/* Parameters: Pointer to tty structure */ +/* Pointer to old termios structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_set_termios( PTTY tty, struct termios *old_termios ) +{ + i2ChanStrPtr pCh = (i2ChanStrPtr)tty->driver_data; + +#ifdef IP2DEBUG_IOCTL + printk (KERN_DEBUG "IP2: set termios %p\n", old_termios ); +#endif + + set_params( pCh, old_termios ); +} + +/******************************************************************************/ +/* Function: ip2_set_line_discipline() */ +/* Parameters: Pointer to tty structure */ +/* Returns: Nothing */ +/* */ +/* Description: Does nothing */ +/* */ +/* */ +/******************************************************************************/ +static void +ip2_set_line_discipline ( PTTY tty ) +{ +#ifdef IP2DEBUG_IOCTL + printk (KERN_DEBUG "IP2: set line discipline\n" ); +#endif +#ifdef IP2DEBUG_TRACE + ip2trace (((i2ChanStrPtr)tty->driver_data)->port_index, ITRC_IOCTL, 16, 0 ); +#endif +} + +/******************************************************************************/ +/* Function: SetLine Characteristics() */ +/* Parameters: Pointer to channel structure */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This routine is called to update the channel structure with the new line */ +/* characteristics, and send the appropriate commands to the board when they */ +/* change. */ +/******************************************************************************/ +static void +set_params( i2ChanStrPtr pCh, struct termios *o_tios ) +{ + tcflag_t cflag, iflag, lflag; + char stop_char, start_char; + struct termios dummy; + + lflag = pCh->pTTY->termios->c_lflag; + cflag = pCh->pTTY->termios->c_cflag; + iflag = pCh->pTTY->termios->c_iflag; + + if (o_tios == NULL) { + dummy.c_lflag = ~lflag; + dummy.c_cflag = ~cflag; + dummy.c_iflag = ~iflag; + o_tios = &dummy; + } + + { + switch ( cflag & CBAUD ) { + case B0: + i2QueueCommands( PTYPE_BYPASS, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN); + pCh->dataSetOut &= ~(I2_DTR | I2_RTS); + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25)); + pCh->pTTY->termios->c_cflag |= (CBAUD & o_tios->c_cflag); + goto service_it; + break; + case B38400: + /* + * This is the speed that is overloaded with all the other high + * speeds, depending upon the flag settings. + */ + if ( ( pCh->flags & ASYNC_SPD_MASK ) == ASYNC_SPD_HI ) { + pCh->speed = CBR_57600; + } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) { + pCh->speed = CBR_115200; + } else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST ) { + pCh->speed = CBR_C1; + } else { + pCh->speed = CBR_38400; + } + break; + case B50: pCh->speed = CBR_50; break; + case B75: pCh->speed = CBR_75; break; + case B110: pCh->speed = CBR_110; break; + case B134: pCh->speed = CBR_134; break; + case B150: pCh->speed = CBR_150; break; + case B200: pCh->speed = CBR_200; break; + case B300: pCh->speed = CBR_300; break; + case B600: pCh->speed = CBR_600; break; + case B1200: pCh->speed = CBR_1200; break; + case B1800: pCh->speed = CBR_1800; break; + case B2400: pCh->speed = CBR_2400; break; + case B4800: pCh->speed = CBR_4800; break; + case B9600: pCh->speed = CBR_9600; break; + case B19200: pCh->speed = CBR_19200; break; + case B57600: pCh->speed = CBR_57600; break; + case B115200: pCh->speed = CBR_115200; break; + case B153600: pCh->speed = CBR_153600; break; + case B230400: pCh->speed = CBR_230400; break; + case B307200: pCh->speed = CBR_307200; break; + case B460800: pCh->speed = CBR_460800; break; + case B921600: pCh->speed = CBR_921600; break; + default: pCh->speed = CBR_9600; break; + } + if ( pCh->speed == CBR_C1 ) { + // Process the custom speed parameters. + int bps = pCh->BaudBase / pCh->BaudDivisor; + if ( bps == 921600 ) { + pCh->speed = CBR_921600; + } else { + bps = bps/10; + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_BAUD_DEF1(bps) ); + } + } + i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_SETBAUD(pCh->speed)); + + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP); + pCh->dataSetOut |= (I2_DTR | I2_RTS); + } + if ( (CSTOPB & cflag) ^ (CSTOPB & o_tios->c_cflag)) + { + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, + CMD_SETSTOP( ( cflag & CSTOPB ) ? CST_2 : CST_1)); + } + if (((PARENB|PARODD) & cflag) ^ ((PARENB|PARODD) & o_tios->c_cflag)) + { + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, + CMD_SETPAR( + (cflag & PARENB ? (cflag & PARODD ? CSP_OD : CSP_EV) : CSP_NP) + ) + ); + } + /* byte size and parity */ + if ( (CSIZE & cflag)^(CSIZE & o_tios->c_cflag)) + { + int datasize; + switch ( cflag & CSIZE ) { + case CS5: datasize = CSZ_5; break; + case CS6: datasize = CSZ_6; break; + case CS7: datasize = CSZ_7; break; + case CS8: datasize = CSZ_8; break; + default: datasize = CSZ_5; break; /* as per serial.c */ + } + i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, CMD_SETBITS(datasize) ); + } + /* Process CTS flow control flag setting */ + if ( (cflag & CRTSCTS) ) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, + 2, CMD_CTSFL_ENAB, CMD_RTSFL_ENAB); + } else { + i2QueueCommands(PTYPE_INLINE, pCh, 100, + 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB); + } + // + // Process XON/XOFF flow control flags settings + // + stop_char = STOP_CHAR(pCh->pTTY); + start_char = START_CHAR(pCh->pTTY); + + //////////// can't be \000 + if (stop_char == __DISABLED_CHAR ) + { + stop_char = ~__DISABLED_CHAR; + } + if (start_char == __DISABLED_CHAR ) + { + start_char = ~__DISABLED_CHAR; + } + ///////////////////////////////// + + if ( o_tios->c_cc[VSTART] != start_char ) + { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXON(start_char)); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXON(start_char)); + } + if ( o_tios->c_cc[VSTOP] != stop_char ) + { + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXOFF(stop_char)); + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXOFF(stop_char)); + } + if (stop_char == __DISABLED_CHAR ) + { + stop_char = ~__DISABLED_CHAR; //TEST123 + goto no_xoff; + } + if ((iflag & (IXOFF))^(o_tios->c_iflag & (IXOFF))) + { + if ( iflag & IXOFF ) { // Enable XOFF output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_XON)); + } else { // Disable XOFF output flow control +no_xoff: + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_NONE)); + } + } + if (start_char == __DISABLED_CHAR ) + { + goto no_xon; + } + if ((iflag & (IXON|IXANY)) ^ (o_tios->c_iflag & (IXON|IXANY))) + { + if ( iflag & IXON ) { + if ( iflag & IXANY ) { // Enable XON/XANY output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XANY)); + } else { // Enable XON output flow control + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XON)); + } + } else { // Disable XON output flow control +no_xon: + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_NONE)); + } + } + if ( (iflag & ISTRIP) ^ ( o_tios->c_iflag & (ISTRIP)) ) + { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_ISTRIP_OPT((iflag & ISTRIP ? 1 : 0))); + } + if ( (iflag & INPCK) ^ ( o_tios->c_iflag & (INPCK)) ) + { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, + CMD_PARCHK((iflag & INPCK) ? CPK_ENAB : CPK_DSAB)); + } + + if ( (iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) + ^ ( o_tios->c_iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) ) + { + char brkrpt = 0; + char parrpt = 0; + + if ( iflag & IGNBRK ) { /* Ignore breaks altogether */ + /* Ignore breaks altogether */ + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_NREP); + } else { + if ( iflag & BRKINT ) { + if ( iflag & PARMRK ) { + brkrpt = 0x0a; // exception an inline triple + } else { + brkrpt = 0x1a; // exception and NULL + } + brkrpt |= 0x04; // flush input + } else { + if ( iflag & PARMRK ) { + brkrpt = 0x0b; //POSIX triple \0377 \0 \0 + } else { + brkrpt = 0x01; // Null only + } + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_REP(brkrpt)); + } + + if (iflag & IGNPAR) { + parrpt = 0x20; + /* would be 2 for not cirrus bug */ + /* would be 0x20 cept for cirrus bug */ + } else { + if ( iflag & PARMRK ) { + /* + * Replace error characters with 3-byte sequence (\0377,\0,char) + */ + parrpt = 0x04 ; + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_ISTRIP_OPT((char)0)); + } else { + parrpt = 0x03; + } + } + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SET_ERROR(parrpt)); + } + if (cflag & CLOCAL) { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP); + pCh->flags &= ~ASYNC_CHECK_CD; + } else { + i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP); + pCh->flags |= ASYNC_CHECK_CD; + } + +#ifdef XXX +do_flags_thing: // This is a test, we don't do the flags thing + + if ( (cflag & CRTSCTS) ) { + cflag |= 014000000000; + } + i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, + CMD_UNIX_FLAGS(iflag,cflag,lflag)); +#endif + +service_it: + i2DrainOutput( pCh, 100 ); +} + +/******************************************************************************/ +/* IPL Device Section */ +/******************************************************************************/ + +/******************************************************************************/ +/* Function: ip2_ipl_read() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Pointer to data */ +/* Number of bytes to read */ +/* Returns: Success or failure */ +/* */ +/* Description: Ugly */ +/* */ +/* */ +/******************************************************************************/ + +static +ssize_t +ip2_ipl_read(struct file *pFile, char *pData, size_t count, loff_t *off ) +{ + unsigned int minor = MINOR( pFile->f_dentry->d_inode->i_rdev ); + int rc = 0; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: read %p, %d bytes\n", pData, count ); +#endif + + switch( minor ) { + case 0: // IPL device + rc = -EINVAL; + break; + case 1: // Status dump + rc = -EINVAL; + break; + case 2: // Ping device + rc = -EINVAL; + break; + case 3: // Trace device + rc = DumpTraceBuffer ( pData, count ); + break; + case 4: // Trace device + rc = DumpFifoBuffer ( pData, count ); + break; + default: + rc = -ENODEV; + break; + } + return rc; +} + +static int +DumpFifoBuffer ( char *pData, int count ) +{ +#ifdef DEBUG_FIFO + int rc=0; + if(copy_to_user(pData, DBGBuf, count)) + rc=-EFAULT; + + printk(KERN_DEBUG "Last index %d\n", I ); + + return count; +#endif /* DEBUG_FIFO */ + return 0; +} + +static int +DumpTraceBuffer ( char *pData, int count ) +{ +#ifdef IP2DEBUG_TRACE + int rc; + int dumpcount; + int chunk; + int *pIndex = (int*)pData; + + if ( count < (sizeof(int) * 6) ) { + return -EIO; + } + put_user(tracewrap, pIndex ); + put_user(TRACEMAX, ++pIndex ); + put_user(tracestrip, ++pIndex ); + put_user(tracestuff, ++pIndex ); + pData += sizeof(int) * 6; + count -= sizeof(int) * 6; + + dumpcount = tracestuff - tracestrip; + if ( dumpcount < 0 ) { + dumpcount += TRACEMAX; + } + if ( dumpcount > count ) { + dumpcount = count; + } + chunk = TRACEMAX - tracestrip; + if ( dumpcount > chunk ) { + rc=copy_to_user(pData, &tracebuf[tracestrip], + chunk * sizeof(tracebuf[0]) )?-EFAULT:0; + pData += chunk * sizeof(tracebuf[0]); + tracestrip = 0; + chunk = dumpcount - chunk; + } else { + chunk = dumpcount; + } + rc=copy_to_user(pData, &tracebuf[tracestrip], + chunk * sizeof(tracebuf[0]) )?-EFAULT:0 + tracestrip += chunk; + tracewrap = 0; + + put_user(tracestrip, ++pIndex ); + put_user(tracestuff, ++pIndex ); + + return dumpcount; +#else + return 0; +#endif +} + +/******************************************************************************/ +/* Function: ip2_ipl_write() */ +/* Parameters: */ +/* Pointer to file structure */ +/* Pointer to data */ +/* Number of bytes to write */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static ssize_t +ip2_ipl_write(struct file *pFile, const char *pData, size_t count, loff_t *off) +{ +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: write %p, %d bytes\n", pData, count ); +#endif + return 0; +} + +/******************************************************************************/ +/* Function: ip2_ipl_ioctl() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Command */ +/* Argument */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg ) +{ + unsigned int iplminor = MINOR(pInode->i_rdev); + int rc = 0; + ULONG *pIndex = (ULONG*)arg; + i2eBordStrPtr pB = i2BoardPtrTable[iplminor / 4]; + i2ChanStrPtr pCh; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg ); +#endif + + switch ( iplminor ) { + case 0: // IPL device + rc = -EINVAL; + break; + case 1: // Status dump + case 5: + case 9: + case 13: + switch ( cmd ) { + case 64: /* Driver - ip2stat */ + put_user(ref_count, pIndex++ ); + put_user(irq_counter, pIndex++ ); + put_user(bh_counter, pIndex++ ); + break; + + case 65: /* Board - ip2stat */ + if ( pB ) { + if(copy_to_user((char*)arg, (char*)pB, sizeof(i2eBordStr) )) + rc=-EFAULT; + put_user(INB(pB->i2eStatus), + (ULONG*)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) ); + } else { + rc = -ENODEV; + } + break; + + default: + pCh = DevTable[cmd]; + if ( pCh ) + { + if(copy_to_user((char*)arg, (char*)pCh, sizeof(i2ChanStr) )) + rc = -EFAULT; + } else { + rc = cmd < 64 ? -ENODEV : -EINVAL; + } + } + break; + + case 2: // Ping device + rc = -EINVAL; + break; + case 3: // Trace device + if ( cmd == 1 ) { + put_user(iiSendPendingMail, pIndex++ ); + put_user(i2InitChannels, pIndex++ ); + put_user(i2QueueNeeds, pIndex++ ); + put_user(i2QueueCommands, pIndex++ ); + put_user(i2GetStatus, pIndex++ ); + put_user(i2Input, pIndex++ ); + put_user(i2InputFlush, pIndex++ ); + put_user(i2Output, pIndex++ ); + put_user(i2FlushOutput, pIndex++ ); + put_user(i2DrainWakeup, pIndex++ ); + put_user(i2DrainOutput, pIndex++ ); + put_user(i2OutputFree, pIndex++ ); + put_user(i2StripFifo, pIndex++ ); + put_user(i2StuffFifoBypass, pIndex++ ); + put_user(i2StuffFifoFlow, pIndex++ ); + put_user(i2StuffFifoInline, pIndex++ ); + put_user(i2ServiceBoard, pIndex++ ); + put_user(serviceOutgoingFifo, pIndex++ ); + // put_user(ip2_init, pIndex++ ); + put_user(ip2_init_board, pIndex++ ); + put_user(find_eisa_board, pIndex++ ); + put_user(set_irq, pIndex++ ); + put_user(ip2_interrupt, pIndex++ ); + put_user(ip2_poll, pIndex++ ); + put_user(service_all_boards, pIndex++ ); + put_user(do_input, pIndex++ ); + put_user(do_status, pIndex++ ); +#ifndef IP2DEBUG_OPEN + put_user(0, pIndex++ ); +#else + put_user(open_sanity_check, pIndex++ ); +#endif + put_user(ip2_open, pIndex++ ); + put_user(ip2_close, pIndex++ ); + put_user(ip2_hangup, pIndex++ ); + put_user(ip2_write, pIndex++ ); + put_user(ip2_putchar, pIndex++ ); + put_user(ip2_flush_chars, pIndex++ ); + put_user(ip2_write_room, pIndex++ ); + put_user(ip2_chars_in_buf, pIndex++ ); + put_user(ip2_flush_buffer, pIndex++ ); + + //put_user(ip2_wait_until_sent, pIndex++ ); + put_user(0, pIndex++ ); + + put_user(ip2_throttle, pIndex++ ); + put_user(ip2_unthrottle, pIndex++ ); + put_user(ip2_ioctl, pIndex++ ); + put_user(get_modem_info, pIndex++ ); + put_user(set_modem_info, pIndex++ ); + put_user(get_serial_info, pIndex++ ); + put_user(set_serial_info, pIndex++ ); + put_user(ip2_set_termios, pIndex++ ); + put_user(ip2_set_line_discipline, pIndex++ ); + put_user(set_params, pIndex++ ); + } else { + rc = -EINVAL; + } + + break; + + default: + rc = -ENODEV; + break; + } + return rc; +} + +/******************************************************************************/ +/* Function: ip2_ipl_open() */ +/* Parameters: Pointer to device inode */ +/* Pointer to file structure */ +/* Returns: Success or failure */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +static int +ip2_ipl_open( struct inode *pInode, struct file *pFile ) +{ + unsigned int iplminor = MINOR(pInode->i_rdev); + i2eBordStrPtr pB; + i2ChanStrPtr pCh; + +#ifdef IP2DEBUG_IPL + printk (KERN_DEBUG "IP2IPL: open\n" ); +#endif + + //MOD_INC_USE_COUNT; // Needs close entry with decrement. + + switch(iplminor) { + // These are the IPL devices + case 0: + case 4: + case 8: + case 12: + break; + + // These are the status devices + case 1: + case 5: + case 9: + case 13: + break; + + // These are the debug devices + case 2: + case 6: + case 10: + case 14: + pB = i2BoardPtrTable[iplminor / 4]; + pCh = (i2ChanStrPtr) pB->i2eChannelPtr; + break; + + // This is the trace device + case 3: + break; + } + return 0; +} +/******************************************************************************/ +/* Function: ip2_read_procmem */ +/* Parameters: */ +/* */ +/* Returns: Length of output */ +/* */ +/* Description: */ +/* Supplies some driver operating parameters */ +/* Not real useful unless your debugging the fifo */ +/* */ +/******************************************************************************/ + +#define LIMIT (PAGE_SIZE - 120) + +int +ip2_read_procmem(char *buf, char **start, off_t offset, int len, int unused) +{ + i2eBordStrPtr pB; + i2ChanStrPtr pCh; + PTTY tty; + int i; + + len = 0; + +#define FMTLINE "%3d: 0x%08x 0x%08x 0%011o 0%011o\n" +#define FMTLIN2 " 0x%04x 0x%04x tx flow 0x%x\n" +#define FMTLIN3 " 0x%04x 0x%04x rc flow\n" + + len += sprintf(buf+len,"\n"); + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + pB = i2BoardPtrTable[i]; + if ( pB ) { + len += sprintf(buf+len,"board %d:\n",i); + len += sprintf(buf+len,"\tFifo rem: %d mty: %x outM %x\n", + pB->i2eFifoRemains,pB->i2eWaitingForEmptyFifo,pB->i2eOutMailWaiting); + } + } + + len += sprintf(buf+len,"#: tty flags, port flags, cflags, iflags\n"); + for (i=0; i < IP2_MAX_PORTS; i++) { + if (len > LIMIT) + break; + pCh = DevTable[i]; + if (pCh) { + tty = pCh->pTTY; + if (tty && tty->count) { + len += sprintf(buf+len,FMTLINE,i,(int)tty->flags,pCh->flags, + tty->termios->c_cflag,tty->termios->c_iflag); + + len += sprintf(buf+len,FMTLIN2, + pCh->outfl.asof,pCh->outfl.room,pCh->channelNeeds); + len += sprintf(buf+len,FMTLIN3,pCh->infl.asof,pCh->infl.room); + } + } + } + return len; +} + +/* + * This is the handler for /proc/tty/driver/ip2 + * + * This stretch of code has been largely plagerized from at least three + * different sources including ip2mkdev.c and a couple of other drivers. + * The bugs are all mine. :-) =mhw= + */ +int ip2_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int i, j, box; + int len = 0; + int boxes = 0; + int ports = 0; + int tports = 0; + off_t begin = 0; + i2eBordStrPtr pB; + + len += sprintf(page, "ip2info: 1.0 driver: %s\n", pcVersion ); + len += sprintf(page+len, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n", + IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR, + IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX); + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + /* This need to be reset for a board by board count... */ + boxes = 0; + pB = i2BoardPtrTable[i]; + if( pB ) { + switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) + { + case POR_ID_FIIEX: + len += sprintf( page+len, "Board %d: EX ports=", i ); + for( box = 0; box < ABS_MAX_BOXES; ++box ) + { + ports = 0; + + if( pB->i2eChannelMap[box] != 0 ) ++boxes; + for( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if( pB->i2eChannelMap[box] & 1<< j ) { + ++ports; + } + } + len += sprintf( page+len, "%d,", ports ); + tports += ports; + } + + --len; /* Backup over that last comma */ + + len += sprintf( page+len, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8 ); + break; + + case POR_ID_II_4: + len += sprintf(page+len, "Board %d: ISA-4 ports=4 boxes=1", i ); + tports = ports = 4; + break; + + case POR_ID_II_8: + len += sprintf(page+len, "Board %d: ISA-8-std ports=8 boxes=1", i ); + tports = ports = 8; + break; + + case POR_ID_II_8R: + len += sprintf(page+len, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i ); + tports = ports = 8; + break; + + default: + len += sprintf(page+len, "Board %d: unknown", i ); + /* Don't try and probe for minor numbers */ + tports = ports = 0; + } + + } else { + /* Don't try and probe for minor numbers */ + len += sprintf(page+len, "Board %d: vacant", i ); + tports = ports = 0; + } + + if( tports ) { + len += sprintf(page+len, " minors=" ); + + for ( box = 0; box < ABS_MAX_BOXES; ++box ) + { + for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if ( pB->i2eChannelMap[box] & (1 << j) ) + { + len += sprintf (page+len,"%d,", + j + ABS_BIGGEST_BOX * + (box+i*ABS_MAX_BOXES)); + } + } + } + + page[ len - 1 ] = '\n'; /* Overwrite that last comma */ + } else { + len += sprintf (page+len,"\n" ); + } + + if (len+begin > off+count) + break; + if (len+begin < off) { + begin += len; + len = 0; + } + } + + if (i >= IP2_MAX_BOARDS) + *eof = 1; + if (off >= len+begin) + return 0; + + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); + } + +/******************************************************************************/ +/* Function: ip2trace() */ +/* Parameters: Value to add to trace buffer */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* */ +/* */ +/******************************************************************************/ +void +ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned long codes, ...) +{ +#ifdef IP2DEBUG_TRACE + long flags; + unsigned long *pCode = &codes; + union ip2breadcrumb bc; + i2ChanStrPtr pCh; + + + tracebuf[tracestuff++] = jiffies; + if ( tracestuff == TRACEMAX ) { + tracestuff = 0; + } + if ( tracestuff == tracestrip ) { + if ( ++tracestrip == TRACEMAX ) { + tracestrip = 0; + } + ++tracewrap; + } + + bc.hdr.port = 0xff & pn; + bc.hdr.cat = cat; + bc.hdr.codes = (unsigned char)( codes & 0xff ); + bc.hdr.label = label; + tracebuf[tracestuff++] = bc.value; + + for (;;) { + if ( tracestuff == TRACEMAX ) { + tracestuff = 0; + } + if ( tracestuff == tracestrip ) { + if ( ++tracestrip == TRACEMAX ) { + tracestrip = 0; + } + ++tracewrap; + } + + if ( !codes-- ) + break; + + tracebuf[tracestuff++] = *++pCode; + } +#endif +} + + diff -u --recursive --new-file v2.2.11/linux/drivers/char/isicom.c linux/drivers/char/isicom.c --- v2.2.11/linux/drivers/char/isicom.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/isicom.c Wed Aug 25 17:29:47 1999 @@ -1808,6 +1808,7 @@ static int register_isr(void) { int count, done=0, card; + int flag; unsigned char request; for (count=0; count < BOARD_COUNT; count++ ) { if (isi_card[count].base) { @@ -1829,8 +1830,12 @@ isi_card[count].base = 0; break; } + flag=0; + if(isi_card[count].isa == NO) + flag |= SA_SHIRQ; + if (request == YES) { - if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT, ISICOM_NAME, NULL)) { + if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT|flag, ISICOM_NAME, NULL)) { printk(KERN_WARNING "ISICOM: Could not install handler at Irq %d. Card%d will be disabled.\n", isi_card[count].irq, count+1); release_region(isi_card[count].base,16); diff -u --recursive --new-file v2.2.11/linux/drivers/char/nvram.c linux/drivers/char/nvram.c --- v2.2.11/linux/drivers/char/nvram.c Mon Aug 24 13:14:09 1998 +++ linux/drivers/char/nvram.c Wed Aug 25 17:29:47 1999 @@ -479,7 +479,7 @@ #ifdef CONFIG_PROC_FS static char *floppy_types[] = { - "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M" + "none", "5.25'' 360k", "5.25'' 1.2M", "3.5'' 720k", "3.5'' 1.44M", "3.5'' 2.88M" }; static char *gfx_types[] = { @@ -521,14 +521,14 @@ PRINT_PROC( "HD 0 type : " ); type = nvram[4] >> 4; if (type) - PRINT_PROC( " %02x\n", type == 0x0f ? nvram[11] : type ); + PRINT_PROC( "%02x\n", type == 0x0f ? nvram[11] : type ); else PRINT_PROC( "none\n" ); PRINT_PROC( "HD 1 type : " ); type = nvram[4] & 0x0f; if (type) - PRINT_PROC( " %02x\n", type == 0x0f ? nvram[12] : type ); + PRINT_PROC( "%02x\n", type == 0x0f ? nvram[12] : type ); else PRINT_PROC( "none\n" ); diff -u --recursive --new-file v2.2.11/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c --- v2.2.11/linux/drivers/char/pcwd.c Mon Aug 24 13:14:10 1998 +++ linux/drivers/char/pcwd.c Wed Aug 25 17:29:47 1999 @@ -243,7 +243,10 @@ return i ? -EFAULT : 0; case WDIOC_GETSTATUS: + if (revision == PCWD_REVISION_A) cdat = inb(current_readport); + else + cdat = inb(current_readport + 1 ); rv = 0; if (revision == PCWD_REVISION_A) diff -u --recursive --new-file v2.2.11/linux/drivers/char/planb.c linux/drivers/char/planb.c --- v2.2.11/linux/drivers/char/planb.c Mon May 10 10:17:28 1999 +++ linux/drivers/char/planb.c Wed Aug 25 17:29:47 1999 @@ -1533,7 +1533,8 @@ DEBUG("PlanB: IOCTL VIDIOCSFBUF\n"); - if (!capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) + || !capable(CAP_SYS_RAWIO)) return -EPERM; if (copy_from_user(&v, arg,sizeof(v))) return -EFAULT; diff -u --recursive --new-file v2.2.11/linux/drivers/char/saa7111.c linux/drivers/char/saa7111.c --- v2.2.11/linux/drivers/char/saa7111.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/saa7111.c Wed Aug 25 17:29:47 1999 @@ -0,0 +1,421 @@ +/* + saa7111 - Philips SAA7111A video decoder driver version 0.0.3 + + Copyright (C) 1998 Dave Perks + + 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 + +#define DEBUG(x) /* Debug driver */ + +/* ----------------------------------------------------------------------- */ + +struct saa7111 { + struct i2c_bus *bus; + int addr; + unsigned char reg[32]; + + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +#define I2C_SAA7111 0x48 + +#define I2C_DELAY 10 + +/* ----------------------------------------------------------------------- */ + +static int saa7111_write(struct saa7111 *dev, unsigned char subaddr, unsigned char data) +{ + int ack; + unsigned long flags; + + LOCK_I2C_BUS(dev->bus); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + i2c_sendbyte(dev->bus, subaddr, I2C_DELAY); + ack = i2c_sendbyte(dev->bus, data, I2C_DELAY); + dev->reg[subaddr] = data; + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + return ack; +} + +static int saa7111_write_block(struct saa7111 *dev, unsigned const char *data, unsigned int len) +{ + int ack; + unsigned subaddr; + unsigned long flags; + + while (len > 1) { + LOCK_I2C_BUS(dev->bus); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + ack = i2c_sendbyte(dev->bus, (subaddr = *data++), I2C_DELAY); + ack = i2c_sendbyte(dev->bus, (dev->reg[subaddr] = *data++), I2C_DELAY); + len -= 2; + while (len > 1 && *data == ++subaddr) { + data++; + ack = i2c_sendbyte(dev->bus, (dev->reg[subaddr] = *data++), I2C_DELAY); + len -= 2; + } + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + } + return ack; +} + +static int saa7111_read(struct saa7111 *dev, unsigned char subaddr) +{ + int data; + unsigned long flags; + + LOCK_I2C_BUS(dev->bus); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + i2c_sendbyte(dev->bus, subaddr, I2C_DELAY); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr | 1, I2C_DELAY); + data = i2c_readbyte(dev->bus, 1); + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + return data; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7111_attach(struct i2c_device *device) +{ + int i; + struct saa7111 *decoder; + + static const unsigned char init[] = + { + 0x00, 0x00, /* 00 - ID byte */ + 0x01, 0x00, /* 01 - reserved */ + + /*front end */ + 0x02, 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */ + 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ + 0x04, 0x00, /* 04 - GAI1=256 */ + 0x05, 0x00, /* 05 - GAI2=256 */ + + /* decoder */ + 0x06, 0xf6, /* 06 - HSB at 13(50Hz) / 17(60Hz) pixels after end of last line */ + 0x07, 0xdd, /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */ + 0x08, 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, HPLL=0, VNOI=0 */ + 0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */ + 0x0a, 0x80, /* 0a - BRIG=128 */ + 0x0b, 0x47, /* 0b - CONT=1.109 */ + 0x0c, 0x40, /* 0c - SATN=1.0 */ + 0x0d, 0x00, /* 0d - HUE=0 */ + 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ + 0x0f, 0x00, /* 0f - reserved */ + 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ + 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ + 0x12, 0x00, /* 12 - output control 2 */ + 0x13, 0x00, /* 13 - output control 3 */ + 0x14, 0x00, /* 14 - reserved */ + 0x15, 0x00, /* 15 - VBI */ + 0x16, 0x00, /* 16 - VBI */ + 0x17, 0x00, /* 17 - VBI */ + }; + + device->data = decoder = kmalloc(sizeof(struct saa7111), GFP_KERNEL); + if (decoder == NULL) { + return -ENOMEM; + } + MOD_INC_USE_COUNT; + + memset(decoder, 0, sizeof(struct saa7111)); + strcpy(device->name, "saa7111"); + decoder->bus = device->bus; + decoder->addr = device->addr; + decoder->norm = VIDEO_MODE_NTSC; + decoder->input = 0; + decoder->enable = 1; + decoder->bright = 32768; + decoder->contrast = 32768; + decoder->hue = 32768; + decoder->sat = 32768; + + i = saa7111_write_block(decoder, init, sizeof(init)); + if (i < 0) { + printk(KERN_ERR "%s_attach: init status %d\n", device->name, i); + } else { + printk(KERN_INFO "%s_attach: chip version %x\n", device->name, saa7111_read(decoder, 0x00)); + } + return 0; +} + + +static int saa7111_detach(struct i2c_device *device) +{ + kfree(device->data); + MOD_DEC_USE_COUNT; + return 0; +} + +static int saa7111_command(struct i2c_device *device, unsigned int cmd, void *arg) +{ + struct saa7111 *decoder = device->data; + + switch (cmd) { + +#if defined(DECODER_DUMP) + case DECODER_DUMP: + { + int i; + + for (i = 0; i < 32; i += 16) { + int j; + + printk("KERN_DEBUG %s: %03x", device->name, i); + for (j = 0; j < 16; ++j) { + printk(" %02x", saa7111_read(decoder, i + j)); + } + printk("\n"); + } + } + break; +#endif /* defined(DECODER_DUMP) */ + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *cap = arg; + + cap->flags + = VIDEO_DECODER_PAL + | VIDEO_DECODER_NTSC + | VIDEO_DECODER_AUTO + | VIDEO_DECODER_CCIR; + cap->inputs = 8; + cap->outputs = 1; + } + break; + + case DECODER_GET_STATUS: + { + int *iarg = arg; + int status; + int res; + + status = saa7111_read(decoder, 0x1f); + res = 0; + if ((status & (1 << 6)) == 0) { + res |= DECODER_STATUS_GOOD; + } + switch (decoder->norm) { + case VIDEO_MODE_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case VIDEO_MODE_PAL: + res |= DECODER_STATUS_PAL; + break; + default: + case VIDEO_MODE_AUTO: + if ((status & (1 << 5)) != 0) { + res |= DECODER_STATUS_NTSC; + } else { + res |= DECODER_STATUS_PAL; + } + break; + } + if ((status & (1 << 0)) != 0) { + res |= DECODER_STATUS_COLOR; + } + *iarg = res; + } + break; + + case DECODER_SET_NORM: + { + int *iarg = arg; + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0x3f) | 0x40); + break; + + case VIDEO_MODE_PAL: + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0x3f) | 0x00); + break; + + case VIDEO_MODE_AUTO: + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0x3f) | 0x80); + break; + + default: + return -EINVAL; + + } + decoder->norm = *iarg; + } + break; + + case DECODER_SET_INPUT: + { + int *iarg = arg; + + if (*iarg < 0 || *iarg > 7) { + return -EINVAL; + } + if (decoder->input != *iarg) { + decoder->input = *iarg; + /* select mode */ + saa7111_write(decoder, 0x02, (decoder->reg[0x02] & 0xf8) | decoder->input); + /* bypass chrominance trap for modes 4..7 */ + saa7111_write(decoder, 0x09, (decoder->reg[0x09] & 0x7f) | ((decoder->input > 3) ? 0x80 : 0)); + } + } + break; + + case DECODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case DECODER_ENABLE_OUTPUT: + { + int *iarg = arg; + int enable = (*iarg != 0); + + if (decoder->enable != enable) { + decoder->enable = enable; + +// RJ: If output should be disabled (for playing videos), we also need a open PLL. + // The input is set to 0 (where no input source is connected), although this + // is not necessary. + // + // If output should be enabled, we have to reverse the above. + + if (decoder->enable) { + saa7111_write(decoder, 0x02, (decoder->reg[0x02] & 0xf8) | decoder->input); + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0xfb)); + saa7111_write(decoder, 0x11, (decoder->reg[0x11] & 0xf3) | 0x0c); + } else { + saa7111_write(decoder, 0x02, (decoder->reg[0x02] & 0xf8)); + saa7111_write(decoder, 0x08, (decoder->reg[0x08] & 0xfb) | 0x04); + saa7111_write(decoder, 0x11, (decoder->reg[0x11] & 0xf3)); + } + } + } + break; + + case DECODER_SET_PICTURE: + { + struct video_picture *pic = arg; + + if (decoder->bright != pic->brightness) { + /* We want 0 to 255 we get 0-65535 */ + decoder->bright = pic->brightness; + saa7111_write(decoder, 0x0a, decoder->bright >> 8); + } + if (decoder->contrast != pic->contrast) { + /* We want 0 to 127 we get 0-65535 */ + decoder->contrast = pic->contrast; + saa7111_write(decoder, 0x0b, decoder->contrast >> 9); + } + if (decoder->sat != pic->colour) { + /* We want 0 to 127 we get 0-65535 */ + decoder->sat = pic->colour; + saa7111_write(decoder, 0x0c, decoder->sat >> 9); + } + if (decoder->hue != pic->hue) { + /* We want -128 to 127 we get 0-65535 */ + decoder->hue = pic->hue; + saa7111_write(decoder, 0x0d, (decoder->hue - 32768) >> 8); + } + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +struct i2c_driver i2c_driver_saa7111 = +{ + "saa7111", /* name */ + I2C_DRIVERID_VIDEODECODER, /* ID */ + I2C_SAA7111, I2C_SAA7111 + 1, + + saa7111_attach, + saa7111_detach, + saa7111_command +}; + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +int init_module(void) +#else +int saa7111_init(void) +#endif +{ + return i2c_register_driver(&i2c_driver_saa7111); +} + + + +#ifdef MODULE + +void cleanup_module(void) +{ + i2c_unregister_driver(&i2c_driver_saa7111); +} + +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/saa7185.c linux/drivers/char/saa7185.c --- v2.2.11/linux/drivers/char/saa7185.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/saa7185.c Wed Aug 25 17:29:47 1999 @@ -0,0 +1,379 @@ +/* + saa7185 - Philips SAA7185B video encoder driver version 0.0.3 + + Copyright (C) 1998 Dave Perks + + 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 + +#define DEBUG(x) x /* Debug driver */ + +/* ----------------------------------------------------------------------- */ + +struct saa7185 { + struct i2c_bus *bus; + int addr; + unsigned char reg[128]; + + int norm; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +#define I2C_SAA7185 0x88 + +#define I2C_DELAY 10 + +/* ----------------------------------------------------------------------- */ + +static int saa7185_write(struct saa7185 *dev, unsigned char subaddr, unsigned char data) +{ + int ack; + unsigned long flags; + + LOCK_I2C_BUS(dev->bus); + + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + i2c_sendbyte(dev->bus, subaddr, I2C_DELAY); + ack = i2c_sendbyte(dev->bus, data, I2C_DELAY); + dev->reg[subaddr] = data; + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + return ack; +} + +static int saa7185_write_block(struct saa7185 *dev, unsigned const char *data, unsigned int len) +{ + int ack; + unsigned subaddr; + unsigned long flags; + + while (len > 1) { + LOCK_I2C_BUS(dev->bus); + i2c_start(dev->bus); + i2c_sendbyte(dev->bus, dev->addr, I2C_DELAY); + ack = i2c_sendbyte(dev->bus, (subaddr = *data++), I2C_DELAY); + ack = i2c_sendbyte(dev->bus, (dev->reg[subaddr] = *data++), I2C_DELAY); + len -= 2; + while (len > 1 && *data == ++subaddr) { + data++; + ack = i2c_sendbyte(dev->bus, (dev->reg[subaddr] = *data++), I2C_DELAY); + len -= 2; + } + i2c_stop(dev->bus); + UNLOCK_I2C_BUS(dev->bus); + } + return ack; +} + +/* ----------------------------------------------------------------------- */ + +static const unsigned char init_common[] = +{ + 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, YUV2C=1, MY2C=1, MUV2C=1 */ + + 0x42, 0x6b, /* OVLY0=107 */ + 0x43, 0x00, /* OVLU0=0 white */ + 0x44, 0x00, /* OVLV0=0 */ + 0x45, 0x22, /* OVLY1=34 */ + 0x46, 0xac, /* OVLU1=172 yellow */ + 0x47, 0x0e, /* OVLV1=14 */ + 0x48, 0x03, /* OVLY2=3 */ + 0x49, 0x1d, /* OVLU2=29 cyan */ + 0x4a, 0xac, /* OVLV2=172 */ + 0x4b, 0xf0, /* OVLY3=240 */ + 0x4c, 0xc8, /* OVLU3=200 green */ + 0x4d, 0xb9, /* OVLV3=185 */ + 0x4e, 0xd4, /* OVLY4=212 */ + 0x4f, 0x38, /* OVLU4=56 magenta */ + 0x50, 0x47, /* OVLV4=71 */ + 0x51, 0xc1, /* OVLY5=193 */ + 0x52, 0xe3, /* OVLU5=227 red */ + 0x53, 0x54, /* OVLV5=84 */ + 0x54, 0xa3, /* OVLY6=163 */ + 0x55, 0x54, /* OVLU6=84 blue */ + 0x56, 0xf2, /* OVLV6=242 */ + 0x57, 0x90, /* OVLY7=144 */ + 0x58, 0x00, /* OVLU7=0 black */ + 0x59, 0x00, /* OVLV7=0 */ + + 0x5a, 0x00, /* CHPS=0 */ + 0x5b, 0x76, /* GAINU=118 */ + 0x5c, 0xa5, /* GAINV=165 */ + 0x5d, 0x3c, /* BLCKL=60 */ + 0x5e, 0x3a, /* BLNNL=58 */ + 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ + 0x60, 0x00, /* NULL */ + +/* 0x61 - 0x66 set according to norm */ + + 0x67, 0x00, /* 0 : caption 1st byte odd field */ + 0x68, 0x00, /* 0 : caption 2nd byte odd field */ + 0x69, 0x00, /* 0 : caption 1st byte even field */ + 0x6a, 0x00, /* 0 : caption 2nd byte even field */ + + 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ + 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, CBLF=0, ORCV2=0, PRCV2=0 */ + 0x6d, 0x00, /* SRCM1=0, CCEN=0 */ + + 0x6e, 0x0e, /* HTRIG=0x00e, approx. centered, at least for PAL */ + 0x6f, 0x00, /* HTRIG upper bits */ + 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ + +/* The following should not be needed */ + + 0x71, 0x15, /* BMRQ=0x115 */ + 0x72, 0x90, /* EMRQ=0x690 */ + 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */ + 0x74, 0x00, /* NULL */ + 0x75, 0x00, /* NULL */ + 0x76, 0x00, /* NULL */ + 0x77, 0x15, /* BRCV=0x115 */ + 0x78, 0x90, /* ERCV=0x690 */ + 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ + +/* Field length controls */ + + 0x7a, 0x70, /* FLC=0 */ + +/* The following should not be needed if SBLN = 1 */ + + 0x7b, 0x16, /* FAL=22 */ + 0x7c, 0x35, /* LAL=244 */ + 0x7d, 0x20, /* LAL=244, FAL=22 */ +}; + +static const unsigned char init_pal[] = +{ + 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xc8, /* DECTYP=1, BSTA=72 */ + 0x63, 0xcb, /* FSC0 */ + 0x64, 0x8a, /* FSC1 */ + 0x65, 0x09, /* FSC2 */ + 0x66, 0x2a, /* FSC3 */ +}; + +static const unsigned char init_ntsc[] = +{ + 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xe6, /* DECTYP=1, BSTA=102 */ + 0x63, 0x1f, /* FSC0 */ + 0x64, 0x7c, /* FSC1 */ + 0x65, 0xf0, /* FSC2 */ + 0x66, 0x21, /* FSC3 */ +}; + +static int saa7185_attach(struct i2c_device *device) +{ + int i; + struct saa7185 *encoder; + + device->data = encoder = kmalloc(sizeof(struct saa7185), GFP_KERNEL); + if (encoder == NULL) { + return -ENOMEM; + } + MOD_INC_USE_COUNT; + + memset(encoder, 0, sizeof(struct saa7185)); + strcpy(device->name, "saa7185"); + encoder->bus = device->bus; + encoder->addr = device->addr; + encoder->norm = VIDEO_MODE_NTSC; + encoder->enable = 1; + + i = saa7185_write_block(encoder, init_common, sizeof(init_common)); + if (i >= 0) { + i = saa7185_write_block(encoder, init_ntsc, sizeof(init_ntsc)); + } + if (i < 0) { + printk(KERN_ERR "%s_attach: init error %d\n", device->name, i); + } + return 0; +} + + +static int saa7185_detach(struct i2c_device *device) +{ + kfree(device->data); + MOD_DEC_USE_COUNT; + return 0; +} + +static int saa7185_command(struct i2c_device *device, unsigned int cmd, void *arg) +{ + struct saa7185 *encoder = device->data; + + switch (cmd) { + + case ENCODER_GET_CAPABILITIES: + { + struct video_encoder_capability *cap = arg; + + cap->flags + = VIDEO_ENCODER_PAL + | VIDEO_ENCODER_NTSC + | VIDEO_ENCODER_SECAM + | VIDEO_ENCODER_CCIR; + cap->inputs = 1; + cap->outputs = 1; + } + break; + + case ENCODER_SET_NORM: + { + int *iarg = arg; + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + saa7185_write_block(encoder, init_ntsc, sizeof(init_ntsc)); + break; + + case VIDEO_MODE_PAL: + saa7185_write_block(encoder, init_pal, sizeof(init_pal)); + break; + + case VIDEO_MODE_SECAM: + default: + return -EINVAL; + + } + encoder->norm = *iarg; + } + break; + + case ENCODER_SET_INPUT: + { + int *iarg = arg; + +#if 0 + /* not much choice of inputs */ + if (*iarg != 0) { + return -EINVAL; + } +#else + /* RJ: *iarg = 0: input is from SA7111 + *iarg = 1: input is from ZR36060 */ + + switch (*iarg) { + + case 0: + /* Switch RTCE to 1 */ + saa7185_write(encoder, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); + break; + + case 1: + /* Switch RTCE to 0 */ + saa7185_write(encoder, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00); + break; + + default: + return -EINVAL; + + } +#endif + } + break; + + case ENCODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case ENCODER_ENABLE_OUTPUT: + { + int *iarg = arg; + + encoder->enable = !!*iarg; + saa7185_write(encoder, 0x61, (encoder->reg[0x61] & 0xbf) | (encoder->enable ? 0x00 : 0x40)); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +struct i2c_driver i2c_driver_saa7185 = +{ + "saa7185", /* name */ + I2C_DRIVERID_VIDEOENCODER, /* ID */ + I2C_SAA7185, I2C_SAA7185 + 1, + + saa7185_attach, + saa7185_detach, + saa7185_command +}; + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +int init_module(void) +#else +int saa7185_init(void) +#endif +{ + return i2c_register_driver(&i2c_driver_saa7185); +} + + + +#ifdef MODULE + +void cleanup_module(void) +{ + i2c_unregister_driver(&i2c_driver_saa7185); +} + +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.2.11/linux/drivers/char/tty_io.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/char/tty_io.c Wed Aug 25 17:29:47 1999 @@ -2136,6 +2136,9 @@ #ifdef CONFIG_SERIAL rs_init(); #endif +#ifdef CONFIG_COMPUTONE + ip2_init(); +#endif #ifdef CONFIG_MAC_SERIAL macserial_init(); #endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/zr36057.h linux/drivers/char/zr36057.h --- v2.2.11/linux/drivers/char/zr36057.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/zr36057.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,168 @@ +/* + zr36057.h - zr36057 register offsets + + Copyright (C) 1998 Dave Perks + + 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 _ZR36057_H_ +#define _ZR36057_H_ + + +/* Zoran ZR36057 registers */ + +#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ +#define ZR36057_VFEHCR_HSPol (1<<30) +#define ZR36057_VFEHCR_HStart 10 +#define ZR36057_VFEHCR_HEnd 0 +#define ZR36057_VFEHCR_Hmask 0x3ff + +#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ +#define ZR36057_VFEVCR_VSPol (1<<30) +#define ZR36057_VFEVCR_VStart 10 +#define ZR36057_VFEVCR_VEnd 0 +#define ZR36057_VFEVCR_Vmask 0x3ff + +#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ +#define ZR36057_VFESPFR_ExtFl (1<<26) +#define ZR36057_VFESPFR_TopField (1<<25) +#define ZR36057_VFESPFR_VCLKPol (1<<24) +#define ZR36057_VFESPFR_HFilter 21 +#define ZR36057_VFESPFR_HorDcm 14 +#define ZR36057_VFESPFR_VerDcm 8 +#define ZR36057_VFESPFR_DispMode 6 +#define ZR36057_VFESPFR_YUV422 (0<<3) +#define ZR36057_VFESPFR_RGB888 (1<<3) +#define ZR36057_VFESPFR_RGB565 (2<<3) +#define ZR36057_VFESPFR_RGB555 (3<<3) +#define ZR36057_VFESPFR_ErrDif (1<<2) +#define ZR36057_VFESPFR_Pack24 (1<<1) +#define ZR36057_VFESPFR_LittleEndian (1<<0) + +#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ + +#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ + +#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ +#define ZR36057_VSSFGR_DispStride 16 +#define ZR36057_VSSFGR_VidOvf (1<<8) +#define ZR36057_VSSFGR_SnapShot (1<<1) +#define ZR36057_VSSFGR_FrameGrab (1<<0) + +#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ +#define ZR36057_VDCR_VidEn (1<<31) +#define ZR36057_VDCR_MinPix 24 +#define ZR36057_VDCR_Triton (1<<24) +#define ZR36057_VDCR_VidWinHt 12 +#define ZR36057_VDCR_VidWinWid 0 + +#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ + +#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ + +#define ZR36057_OCR 0x024 /* Overlay Control Register */ +#define ZR36057_OCR_OvlEnable (1 << 15) +#define ZR36057_OCR_MaskStride 0 + +#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ +#define ZR36057_SPGPPCR_SoftReset (1<<24) + +#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ + +#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ + +#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ +#define ZR36057_MCTCR_CodTime (1 << 30) +#define ZR36057_MCTCR_CEmpty (1 << 29) +#define ZR36057_MCTCR_CFlush (1 << 28) +#define ZR36057_MCTCR_CodGuestID 20 +#define ZR36057_MCTCR_CodGuestReg 16 + +#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ + +#define ZR36057_ISR 0x03c /* Interrupt Status Register */ +#define ZR36057_ISR_GIRQ1 (1<<30) +#define ZR36057_ISR_GIRQ0 (1<<29) +#define ZR36057_ISR_CodRepIRQ (1<<28) +#define ZR36057_ISR_JPEGRepIRQ (1<<27) + +#define ZR36057_ICR 0x040 /* Interrupt Control Register */ +#define ZR36057_ICR_GIRQ1 (1<<30) +#define ZR36057_ICR_GIRQ0 (1<<29) +#define ZR36057_ICR_CodRepIRQ (1<<28) +#define ZR36057_ICR_JPEGRepIRQ (1<<27) +#define ZR36057_ICR_IntPinEn (1<<24) + +#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ +#define ZR36057_I2CBR_SDA (1<<1) +#define ZR36057_I2CBR_SCL (1<<0) + +#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ +#define ZR36057_JMC_JPG (1 << 31) +#define ZR36057_JMC_JPGExpMode (0 << 29) +#define ZR36057_JMC_JPGCmpMode (1 << 29) +#define ZR36057_JMC_MJPGExpMode (2 << 29) +#define ZR36057_JMC_MJPGCmpMode (3 << 29) +#define ZR36057_JMC_RTBUSY_FB (1 << 6) +#define ZR36057_JMC_Go_en (1 << 5) +#define ZR36057_JMC_SyncMstr (1 << 4) +#define ZR36057_JMC_Fld_per_buff (1 << 3) +#define ZR36057_JMC_VFIFO_FB (1 << 2) +#define ZR36057_JMC_CFIFO_FB (1 << 1) +#define ZR36057_JMC_Stll_LitEndian (1 << 0) + +#define ZR36057_JPC 0x104 /* JPEG Process Control */ +#define ZR36057_JPC_P_Reset (1 << 7) +#define ZR36057_JPC_CodTrnsEn (1 << 5) +#define ZR36057_JPC_Active (1 << 0) + +#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ +#define ZR36057_VSP_VsyncSize 16 +#define ZR36057_VSP_FrmTot 0 + +#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ +#define ZR36057_HSP_HsyncStart 16 +#define ZR36057_HSP_LineTot 0 + +#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ +#define ZR36057_FHAP_NAX 16 +#define ZR36057_FHAP_PAX 0 + +#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ +#define ZR36057_FVAP_NAY 16 +#define ZR36057_FVAP_PAY 0 + +#define ZR36057_FPP 0x118 /* Field Process Parameters */ +#define ZR36057_FPP_Odd_Even (1 << 0) + +#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ + +#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ + +#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ +#define ZR36057_JCGI_JPEGuestID 4 +#define ZR36057_JCGI_JPEGuestReg 0 + +#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ + +#define ZR36057_POR 0x200 /* Post Office Register */ +#define ZR36057_POR_POPen (1<<25) +#define ZR36057_POR_POTime (1<<24) +#define ZR36057_POR_PODir (1<<23) + +#define ZR36057_STR 0x300 /* "Still" Transfer Register */ + +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/char/zr36060.h linux/drivers/char/zr36060.h --- v2.2.11/linux/drivers/char/zr36060.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/zr36060.h Wed Aug 25 17:29:47 1999 @@ -0,0 +1,35 @@ +/* + zr36060.h - zr36060 register offsets + + Copyright (C) 1998 Dave Perks + + 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 _ZR36060_H_ +#define _ZR36060_H_ + + +/* Zoran ZR36060 registers */ + +#define ZR36060_LoadParameters 0x000 +#define ZR36060_Load (1<<7) +#define ZR36060_SyncRst (1<<0) + +#define ZR36060_CodeFifoStatus 0x001 +#define ZR36060_Load (1<<7) +#define ZR36060_SyncRst (1<<0) + +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in --- v2.2.11/linux/drivers/isdn/Config.in Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/Config.in Wed Aug 25 17:29:47 1999 @@ -9,6 +9,9 @@ fi fi bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO +if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then + bool 'Support AT-Fax Class 2 commands' CONFIG_ISDN_TTY_FAX +fi bool 'Support isdn diversion services' CONFIG_ISDN_DIVERSION if [ "$CONFIG_X25" != "n" ]; then bool 'X.25 PLP on top of ISDN (EXPERIMENTAL)' CONFIG_ISDN_X25 @@ -61,6 +64,9 @@ dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN fi dep_tristate 'Eicon.Diehl active card support' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN +if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then + bool 'Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA +fi dep_tristate 'AVM CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then bool 'AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile --- v2.2.11/linux/drivers/isdn/Makefile Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/Makefile Wed Aug 25 17:29:47 1999 @@ -24,6 +24,9 @@ endif ifdef CONFIG_ISDN_AUDIO L_OBJS += isdn_audio.o + ifdef CONFIG_ISDN_TTY_FAX + L_OBJS += isdn_ttyfax.o + endif endif else ifeq ($(CONFIG_ISDN),m) @@ -41,6 +44,9 @@ endif ifdef CONFIG_ISDN_AUDIO O_OBJS += isdn_audio.o + ifdef CONFIG_ISDN_TTY_FAX + O_OBJS += isdn_ttyfax.o + endif endif endif endif diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/avmb1/Makefile linux/drivers/isdn/avmb1/Makefile --- v2.2.11/linux/drivers/isdn/avmb1/Makefile Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/avmb1/Makefile Wed Aug 25 17:29:47 1999 @@ -1,5 +1,5 @@ # -# $Id: Makefile,v 1.5 1999/07/01 15:26:20 calle Exp $ +# $Id: Makefile,v 1.6 1999/07/20 06:41:44 calle Exp $ # # Makefile for the CAPI and AVM-B1 device drivers. # @@ -11,6 +11,10 @@ # parent makes.. # # $Log: Makefile,v $ +# Revision 1.6 1999/07/20 06:41:44 calle +# Bugfix: After the redesign of the AVM B1 driver, the driver didn't even +# compile, if not selected as modules. +# # Revision 1.5 1999/07/01 15:26:20 calle # complete new version (I love it): # + new hardware independed "capi_driver" interface that will make it easy to: @@ -73,7 +77,7 @@ ifeq ($(CONFIG_ISDN_DRV_AVMB1),y) O_TARGET += avmb1.o OX_OBJS += kcapi.o - O_OBJS += capi.o kernelcapi.o + O_OBJS += capi.o ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA O_OBJS += b1isa.o endif diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/avmb1/avmcard.h linux/drivers/isdn/avmb1/avmcard.h --- v2.2.11/linux/drivers/isdn/avmb1/avmcard.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/avmb1/avmcard.h Wed Aug 25 17:29:47 1999 @@ -1,9 +1,15 @@ /* - * $Id: avmcard.h,v 1.2 1999/07/05 15:09:45 calle Exp $ + * $Id: avmcard.h,v 1.4 1999/08/04 10:10:08 calle Exp $ * * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: avmcard.h,v $ + * Revision 1.4 1999/08/04 10:10:08 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.3 1999/07/23 08:41:47 calle + * prepared for new AVM cards. + * * Revision 1.2 1999/07/05 15:09:45 calle * - renamed "appl_release" to "appl_released". * - version und profile data now cleared on controller reset @@ -56,13 +62,22 @@ avm_m1, avm_m2, avm_t1isa, - avm_t1pci + avm_t1pci, + avm_c4 }; +typedef struct avmcard_dmainfo { + __u32 recvlen; + __u8 recvbuf[128+2048]; + struct sk_buff_head send_queue; + __u8 sendbuf[128+2048]; +} avmcard_dmainfo; + typedef struct avmcard { char name[32]; unsigned int port; unsigned irq; + unsigned long membase; enum avmcardtype cardtype; int cardnr; /* for t1isa */ @@ -78,6 +93,10 @@ int interrupt; + void *mbase; + __u32 csr; + avmcard_dmainfo *dma; + struct capi_ctr *ctrl; } avmcard; @@ -174,6 +193,7 @@ * int32 Length message * */ +#define RECEIVE_POLLDWORD 0x75 /* t1pci in dword mode */ #define WRITE_REGISTER 0x00 #define READ_REGISTER 0x01 @@ -511,6 +531,10 @@ b1outp(base, B1_INSTAT, 0x00); b1outp(base, B1_RESET, 0xf0); b1outp(base, B1_INSTAT, 0x02); + break; + case avm_c4: + case avm_t1pci: + b1outp(base, B1_RESET, 0xf0); break; } } diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/avmb1/b1.c linux/drivers/isdn/avmb1/b1.c --- v2.2.11/linux/drivers/isdn/avmb1/b1.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/avmb1/b1.c Wed Aug 25 17:29:47 1999 @@ -1,11 +1,20 @@ /* - * $Id: b1.c,v 1.4 1999/07/09 15:05:38 keil Exp $ + * $Id: b1.c,v 1.7 1999/08/04 10:10:09 calle Exp $ * * Common module for AVM B1 cards. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1.c,v $ + * Revision 1.7 1999/08/04 10:10:09 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.6 1999/07/23 08:51:04 calle + * small fix and typo in checkin before. + * + * Revision 1.5 1999/07/23 08:41:48 calle + * prepared for new AVM cards. + * * Revision 1.4 1999/07/09 15:05:38 keil * compat.h is now isdn_compat.h * @@ -54,7 +63,7 @@ #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.4 $"; +static char *revision = "$Revision: 1.7 $"; /* ------------------------------------------------------------- */ @@ -401,7 +410,10 @@ flag = ((__u8 *)(profp->manu))[1]; switch (flag) { - case 0: strcpy(card->cardname, "B1"); break; + case 0: if (card->version[VER_CARDTYPE]) + strcpy(card->cardname, card->version[VER_CARDTYPE]); + else strcpy(card->cardname, "B1"); + break; case 3: strcpy(card->cardname,"PCMCIA B"); break; case 4: strcpy(card->cardname,"PCMCIA M1"); break; case 5: strcpy(card->cardname,"PCMCIA M2"); break; @@ -580,6 +592,7 @@ case avm_m2: s = "M2"; break; case avm_t1isa: s = "T1 ISA (HEMA)"; break; case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; default: s = "???"; break; } len += sprintf(page+len, "%-16s %s\n", "type", s); @@ -619,15 +632,11 @@ } len += sprintf(page+len, "%-16s %s\n", "cardname", card->cardname); - if (len < off) - return 0; - *eof = 1; - *start = page - off; - return ((count < len-off) ? count : len-off); - if (len < off) + if (off+count >= len) + *eof = 1; + if (len < off) return 0; - *eof = 1; - *start = page - off; + *start = page + off; return ((count < len-off) ? count : len-off); } diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c --- v2.2.11/linux/drivers/isdn/avmb1/b1pci.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/avmb1/b1pci.c Wed Aug 25 17:29:47 1999 @@ -1,11 +1,17 @@ /* - * $Id: b1pci.c,v 1.14 1999/07/09 15:05:41 keil Exp $ + * $Id: b1pci.c,v 1.16 1999/08/11 21:01:07 keil Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ + * Revision 1.16 1999/08/11 21:01:07 keil + * new PCI codefix + * + * Revision 1.15 1999/08/10 16:02:27 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * * Revision 1.14 1999/07/09 15:05:41 keil * compat.h is now isdn_compat.h * @@ -51,7 +57,7 @@ #include "capilli.h" #include "avmcard.h" -static char *revision = "$Revision: 1.14 $"; +static char *revision = "$Revision: 1.16 $"; /* ------------------------------------------------------------- */ @@ -246,7 +252,7 @@ while ((dev = pci_find_device(PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, dev))) { struct capicardparams param; - param.port = dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + param.port = get_pcibase(dev, 1) & PCI_BASE_ADDRESS_IO_MASK; param.irq = dev->irq; printk(KERN_INFO "%s: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c --- v2.2.11/linux/drivers/isdn/avmb1/capidrv.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/avmb1/capidrv.c Wed Aug 25 17:29:47 1999 @@ -1,11 +1,20 @@ /* - * $Id: capidrv.c,v 1.23 1999/07/09 15:05:44 keil Exp $ + * $Id: capidrv.c,v 1.26 1999/08/06 07:41:16 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.26 1999/08/06 07:41:16 calle + * Added the "vbox patch". if (si1 == 1) si2 = 0; + * + * Revision 1.25 1999/08/04 10:10:11 calle + * Bugfix: corrected /proc functions, added structure for new AVM cards. + * + * Revision 1.24 1999/07/20 06:48:02 calle + * Bugfix: firmware version check for D2 trace was too restrictiv. + * * Revision 1.23 1999/07/09 15:05:44 keil * compat.h is now isdn_compat.h * @@ -154,7 +163,7 @@ #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.23 $"; +static char *revision = "$Revision: 1.26 $"; int debugmode = 0; MODULE_AUTHOR("Carsten Paeth "); @@ -285,6 +294,8 @@ case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: return 2; + case ISDN_PROTO_L2_FAX: + return 4; } } @@ -302,6 +313,8 @@ case ISDN_PROTO_L2_V11019: case ISDN_PROTO_L2_V11038: return 1; + case ISDN_PROTO_L2_FAX: + return 4; } } @@ -318,6 +331,8 @@ case ISDN_PROTO_L2_V11038: default: return 0; + case ISDN_PROTO_L2_FAX: + return 4; } } @@ -1017,6 +1032,13 @@ cmd.parm.setup.si2, cmd.parm.setup.eazmsn); + if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) { + printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n", + card->contrnr, + cmd.parm.setup.si2); + cmd.parm.setup.si2 = 0; + } + switch (card->interface.statcallb(&cmd)) { case 0: case 3: @@ -2083,7 +2105,7 @@ avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; avmversion[2] |= version.minormanuversion & 0x0f; - if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 6)) { + if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { printk(KERN_INFO "%s: D2 trace enabled\n", card->name); capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, card->msgid++, @@ -2135,7 +2157,7 @@ avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; avmversion[2] |= version.minormanuversion & 0x0f; - if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 6)) { + if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { printk(KERN_INFO "%s: D2 trace disabled\n", card->name); } else { printk(KERN_INFO "%s: D3 trace disabled\n", card->name); @@ -2189,6 +2211,10 @@ ISDN_FEATURE_L2_V11096 | ISDN_FEATURE_L2_V11019 | ISDN_FEATURE_L2_V11038 | +#if 0 + ISDN_FEATURE_L2_FAX | + ISDN_FEATURE_L3_FAX | +#endif ISDN_FEATURE_P_UNKNOWN; card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); @@ -2312,10 +2338,11 @@ global.nrecvdatapkt, global.nsentctlpkt, global.nsentdatapkt); - if (len < off) + if (off+count >= len) + *eof = 1; + if (len < off) return 0; - *eof = 1; - *start = page -off; + *start = page + off; return ((count < len-off) ? count : len-off); } diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/avmb1/capilli.h linux/drivers/isdn/avmb1/capilli.h --- v2.2.11/linux/drivers/isdn/avmb1/capilli.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/avmb1/capilli.h Wed Aug 25 17:29:47 1999 @@ -1,5 +1,5 @@ /* - * $Id: capilli.h,v 1.2 1999/07/05 15:09:52 calle Exp $ + * $Id: capilli.h,v 1.4 1999/07/23 08:51:05 calle Exp $ * * Kernel CAPI 2.0 Driver Interface for Linux * @@ -25,6 +25,7 @@ unsigned irq; int cardtype; int cardnr; + unsigned int membase; } capicardparams; struct capi_driver; diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/avmb1/kcapi.c linux/drivers/isdn/avmb1/kcapi.c --- v2.2.11/linux/drivers/isdn/avmb1/kcapi.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/avmb1/kcapi.c Wed Aug 25 17:29:47 1999 @@ -1,11 +1,15 @@ /* - * $Id: kcapi.c,v 1.5 1999/07/09 15:05:48 keil Exp $ + * $Id: kcapi.c,v 1.6 1999/07/20 06:41:49 calle Exp $ * * Kernel CAPI 2.0 Module * * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: kcapi.c,v $ + * Revision 1.6 1999/07/20 06:41:49 calle + * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even + * compile, if not selected as modules. + * * Revision 1.5 1999/07/09 15:05:48 keil * compat.h is now isdn_compat.h * @@ -61,7 +65,7 @@ #include #endif -static char *revision = "$Revision: 1.5 $"; +static char *revision = "$Revision: 1.6 $"; /* ------------------------------------------------------------- */ @@ -1462,6 +1466,21 @@ EXPORT_SYMBOL(attach_capi_driver); EXPORT_SYMBOL(detach_capi_driver); +#ifndef MODULE +#ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA +extern int b1isa_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI +extern int b1pci_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA +extern int t1isa_init(void); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA +extern int b1pcmcia_init(void); +#endif +#endif + /* * init / exit functions */ @@ -1497,6 +1516,18 @@ printk(KERN_NOTICE "CAPI-driver Rev%s: loaded\n", rev); #else printk(KERN_NOTICE "CAPI-driver Rev%s: started\n", rev); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1ISA + (void)b1isa_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCI + (void)b1pci_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_T1ISA + (void)t1isa_init(); +#endif +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCMCIA + (void)b1pcmcia_init(); +#endif #endif return 0; } diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/divert/divert_procfs.c linux/drivers/isdn/divert/divert_procfs.c --- v2.2.11/linux/drivers/isdn/divert/divert_procfs.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/divert/divert_procfs.c Wed Aug 25 17:29:47 1999 @@ -1,5 +1,5 @@ /* - * $Id: divert_procfs.c,v 1.3 1999/07/05 20:21:41 werner Exp $ + * $Id: divert_procfs.c,v 1.4 1999/08/06 07:42:48 calle Exp $ * * Filesystem handling for the diversion supplementary services. * @@ -20,6 +20,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: divert_procfs.c,v $ + * Revision 1.4 1999/08/06 07:42:48 calle + * Added COMPAT_HAS_NEW_WAITQ for rd_queue for newer kernels. + * * Revision 1.3 1999/07/05 20:21:41 werner * changes to use diversion sources for all kernel versions. * removed static device, only proc filesystem used @@ -44,6 +47,7 @@ #include #endif #include +#include #include "isdn_divert.h" /*********************************/ @@ -52,7 +56,11 @@ ulong if_used = 0; /* number of interface users */ static struct divert_info *divert_info_head = NULL; /* head of queue */ static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */ +#ifdef COMPAT_HAS_NEW_WAITQ +static wait_queue_head_t rd_queue; +#else static struct wait_queue *rd_queue = 0; /* Queue IO */ +#endif /*********************************/ /* put an info buffer into queue */ @@ -387,6 +395,10 @@ /***************************************************************************/ int divert_dev_init(void) { int i; + +#ifdef COMPAT_HAS_NEW_WAITQ + init_waitqueue_head(&rd_queue); +#endif #ifdef CONFIG_PROC_FS #if (LINUX_VERSION_CODE < 0x020117) diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/eicon/eicon.h linux/drivers/isdn/eicon/eicon.h --- v2.2.11/linux/drivers/isdn/eicon/eicon.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/eicon/eicon.h Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: eicon.h,v 1.7 1999/07/11 17:16:23 armin Exp $ +/* $Id: eicon.h,v 1.8 1999/07/25 15:12:01 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * @@ -21,6 +21,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon.h,v $ + * Revision 1.8 1999/07/25 15:12:01 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.7 1999/07/11 17:16:23 armin * Bugfixes in queue handling. * Added DSP-DTMF decoder functions. @@ -344,6 +348,22 @@ __u16 ref; /* saved reference */ } entity; +#define FAX_MAX_SCANLINE 256 + +typedef struct { + __u8 PrevObject; + __u8 NextObject; + __u8 abLine[FAX_MAX_SCANLINE]; + __u8 abFrame[FAX_MAX_SCANLINE]; + unsigned int LineLen; + unsigned int LineDataLen; + __u32 LineData; + unsigned int NullBytesPos; + __u8 NullByteExist; + int PageCount; + __u8 Dle; + __u8 Eop; +} eicon_ch_fax_buf; typedef struct { int No; /* Channel Number */ @@ -357,6 +377,10 @@ unsigned short ncci; unsigned char l2prot; /* Layer 2 protocol */ unsigned char l3prot; /* Layer 3 protocol */ +#ifdef CONFIG_ISDN_TTY_FAX + T30_s *fax; /* pointer to fax data in LL */ + eicon_ch_fax_buf fax2; /* fax related struct */ +#endif entity e; /* Entity */ char cpn[32]; /* remember cpn */ char oad[32]; /* remember oad */ diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/eicon/eicon_dsp.h linux/drivers/isdn/eicon/eicon_dsp.h --- v2.2.11/linux/drivers/isdn/eicon/eicon_dsp.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/eicon/eicon_dsp.h Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: eicon_dsp.h,v 1.3 1999/07/11 17:16:24 armin Exp $ +/* $Id: eicon_dsp.h,v 1.4 1999/07/25 15:12:02 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * DSP definitions @@ -21,6 +21,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_dsp.h,v $ + * Revision 1.4 1999/07/25 15:12:02 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.3 1999/07/11 17:16:24 armin * Bugfixes in queue handling. * Added DSP-DTMF decoder functions. @@ -308,6 +312,108 @@ returns: - none - */ + +/* ============= FAX ================ */ + +#define EICON_FAXID_LEN 20 + +typedef struct eicon_t30_s { + __u8 code; + __u8 rate; + __u8 resolution; + __u8 format; + __u8 pages_low; + __u8 pages_high; + __u8 atf; + __u8 control_bits_low; + __u8 control_bits_high; + __u8 feature_bits_low; + __u8 feature_bits_high; + __u8 universal_5; + __u8 universal_6; + __u8 universal_7; + __u8 station_id_len; + __u8 head_line_len; + __u8 station_id[EICON_FAXID_LEN]; +/* __u8 head_line[]; */ +} eicon_t30_s; + + /* EDATA transmit messages */ +#define EDATA_T30_DIS 0x01 +#define EDATA_T30_FTT 0x02 +#define EDATA_T30_MCF 0x03 + + /* EDATA receive messages */ +#define EDATA_T30_DCS 0x81 +#define EDATA_T30_TRAIN_OK 0x82 +#define EDATA_T30_EOP 0x83 +#define EDATA_T30_MPS 0x84 +#define EDATA_T30_EOM 0x85 +#define EDATA_T30_DTC 0x86 + +#define T30_FORMAT_SFF 0 +#define T30_FORMAT_ASCII 1 +#define T30_FORMAT_COUNT 2 + +#define T30_CONTROL_BIT_DISABLE_FINE 0x0001 +#define T30_CONTROL_BIT_ENABLE_ECM 0x0002 +#define T30_CONTROL_BIT_ECM_64_BYTES 0x0004 +#define T30_CONTROL_BIT_ENABLE_2D_CODING 0x0008 +#define T30_CONTROL_BIT_ENABLE_T6_CODING 0x0010 +#define T30_CONTROL_BIT_ENABLE_UNCOMPR 0x0020 +#define T30_CONTROL_BIT_ACCEPT_POLLING 0x0040 +#define T30_CONTROL_BIT_REQUEST_POLLING 0x0080 +#define T30_CONTROL_BIT_MORE_DOCUMENTS 0x0100 + +#define T30_CONTROL_BIT_ALL_FEATURES\ + (T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING |\ + T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR) + +#define T30_FEATURE_BIT_FINE 0x0001 +#define T30_FEATURE_BIT_ECM 0x0002 +#define T30_FEATURE_BIT_ECM_64_BYTES 0x0004 +#define T30_FEATURE_BIT_2D_CODING 0x0008 +#define T30_FEATURE_BIT_T6_CODING 0x0010 +#define T30_FEATURE_BIT_UNCOMPR_ENABLED 0x0020 +#define T30_FEATURE_BIT_POLLING 0x0040 + +#define FAX_OBJECT_DOCU 1 +#define FAX_OBJECT_PAGE 2 +#define FAX_OBJECT_LINE 3 + +#define T4_EOL 0x800 +#define T4_EOL_BITSIZE 12 +#define T4_EOL_DWORD (T4_EOL << (32 - T4_EOL_BITSIZE)) +#define T4_EOL_MASK_DWORD ((__u32) -1 << (32 - T4_EOL_BITSIZE)) + +#define SFF_LEN_FLD_SIZE 3 + +#define _DLE_ 0x10 +#define _ETX_ 0x03 + +typedef struct eicon_sff_dochead { + __u32 id __attribute__ ((packed)); + __u8 version __attribute__ ((packed)); + __u8 reserved1 __attribute__ ((packed)); + __u16 userinfo __attribute__ ((packed)); + __u16 pagecount __attribute__ ((packed)); + __u16 off1pagehead __attribute__ ((packed)); + __u32 offnpagehead __attribute__ ((packed)); + __u32 offdocend __attribute__ ((packed)); +} eicon_sff_dochead; + +typedef struct eicon_sff_pagehead { + __u8 pageheadid __attribute__ ((packed)); + __u8 pageheadlen __attribute__ ((packed)); + __u8 resvert __attribute__ ((packed)); + __u8 reshoriz __attribute__ ((packed)); + __u8 coding __attribute__ ((packed)); + __u8 reserved2 __attribute__ ((packed)); + __u16 linelength __attribute__ ((packed)); + __u16 pagelength __attribute__ ((packed)); + __u32 offprevpage __attribute__ ((packed)); + __u32 offnextpage __attribute__ ((packed)); +} eicon_sff_pagehead; #endif /* DSP_H */ diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/eicon/eicon_idi.c linux/drivers/isdn/eicon/eicon_idi.c --- v2.2.11/linux/drivers/isdn/eicon/eicon_idi.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/eicon/eicon_idi.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.c,v 1.10 1999/07/11 17:16:24 armin Exp $ +/* $Id: eicon_idi.c,v 1.11 1999/07/25 15:12:03 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * IDI interface @@ -21,6 +21,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.c,v $ + * Revision 1.11 1999/07/25 15:12:03 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.10 1999/07/11 17:16:24 armin * Bugfixes in queue handling. * Added DSP-DTMF decoder functions. @@ -73,7 +77,7 @@ #undef EICON_FULL_SERVICE_OKTETT -char *eicon_idi_revision = "$Revision: 1.10 $"; +char *eicon_idi_revision = "$Revision: 1.11 $"; eicon_manifbuf *manbuf; @@ -91,6 +95,7 @@ int eicon_idi_manage_assign(eicon_card *card); int eicon_idi_manage_remove(eicon_card *card); +int idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer); int idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan) @@ -141,10 +146,25 @@ case ISDN_PROTO_L2_MODEM: reqbuf->XBuffer.P[l++] = 2; break; + case ISDN_PROTO_L2_FAX: + if (chan->fsm_state == EICON_STATE_IWAIT) + reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */ + else + reqbuf->XBuffer.P[l++] = 2; + break; default: reqbuf->XBuffer.P[l++] = 1; } switch(chan->l3prot) { + case ISDN_PROTO_L3_FAX: +#ifdef CONFIG_ISDN_TTY_FAX + reqbuf->XBuffer.P[l++] = 6; + reqbuf->XBuffer.P[l++] = NLC; + tmp = idi_fill_in_T30(chan, &reqbuf->XBuffer.P[l+1]); + reqbuf->XBuffer.P[l++] = tmp; + l += tmp; + break; +#endif case ISDN_PROTO_L3_TRANS: default: reqbuf->XBuffer.P[l++] = 4; @@ -218,6 +238,14 @@ reqbuf->XBuffer.P[6] = 128; reqbuf->XBuffer.P[7] = 0; break; + case ISDN_PROTO_L2_FAX: + reqbuf->XBuffer.P[2] = 0x10; + reqbuf->XBuffer.P[3] = 0; + reqbuf->XBuffer.P[4] = 0; + reqbuf->XBuffer.P[5] = 0; + reqbuf->XBuffer.P[6] = 128; + reqbuf->XBuffer.P[7] = 0; + break; case ISDN_PROTO_L2_TRANS: switch(chan->l3prot) { case ISDN_PROTO_L3_TRANSDSP: @@ -236,8 +264,8 @@ int idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer) { - struct sk_buff *skb; - struct sk_buff *skb2; + struct sk_buff *skb = 0; + struct sk_buff *skb2 = 0; eicon_REQ *reqbuf; eicon_chan_ptr *chan2; @@ -246,7 +274,11 @@ if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -255,7 +287,7 @@ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ)); if (DebugVar & 8) - printk(KERN_DEBUG "idi_req: Ch%d: 0x%02x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); + printk(KERN_DEBUG "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig"); if (layer) cmd |= 0x700; switch(cmd) { case ASSIGN: @@ -296,6 +328,8 @@ default: if (DebugVar & 1) printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No); + dev_kfree_skb(skb); + dev_kfree_skb(skb2); return(-1); } @@ -372,6 +406,9 @@ chan->fsm_state = EICON_STATE_NULL; if (DebugVar & 8) printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No); +#ifdef CONFIG_ISDN_TTY_FAX + chan->fax = 0; +#endif return(0); } @@ -403,7 +440,11 @@ if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -486,6 +527,14 @@ reqbuf->XBuffer.P[l-2] = 128; reqbuf->XBuffer.P[l-1] = 0; break; + case ISDN_PROTO_L2_FAX: + reqbuf->XBuffer.P[l-6] = 0x10; + reqbuf->XBuffer.P[l-5] = 0; + reqbuf->XBuffer.P[l-4] = 0; + reqbuf->XBuffer.P[l-3] = 0; + reqbuf->XBuffer.P[l-2] = 128; + reqbuf->XBuffer.P[l-1] = 0; + break; case ISDN_PROTO_L2_TRANS: switch(chan->l3prot) { case ISDN_PROTO_L3_TRANSDSP: @@ -833,6 +882,7 @@ } } + int idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int len) { @@ -855,8 +905,12 @@ if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); - return -ENOMEM; + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); + return -ENOMEM; } chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr)); @@ -991,7 +1045,7 @@ if ((DebugVar & 128) || ((DebugVar & 16) && (ind->Ind != 8))) { - printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, + printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No, ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); } @@ -1025,6 +1079,9 @@ cmd.command = ISDN_STAT_DHUP; ccard->interface.statcallb(&cmd); eicon_idi_listen_req(ccard, chan); +#ifdef CONFIG_ISDN_TTY_FAX + chan->fax = 0; +#endif break; case INDICATE_IND: if (DebugVar & 8) @@ -1098,7 +1155,15 @@ cmd.command = ISDN_STAT_DCONN; cmd.arg = chan->No; ccard->interface.statcallb(&cmd); - idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + if (chan->l2prot != ISDN_PROTO_L2_FAX) { + idi_do_req(ccard, chan, IDI_N_CONNECT, 1); + } +#ifdef CONFIG_ISDN_TTY_FAX + else { + if (chan->fax) + chan->fax->phase = ISDN_FAX_PHASE_A; + } +#endif } else idi_hangup(ccard, chan); break; @@ -1113,6 +1178,12 @@ ccard->interface.statcallb(&cmd); idi_do_req(ccard, chan, ASSIGN, 1); idi_do_req(ccard, chan, IDI_N_CONNECT, 1); +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + if (chan->fax) + chan->fax->phase = ISDN_FAX_PHASE_A; + } +#endif } else idi_hangup(ccard, chan); break; @@ -1142,6 +1213,27 @@ chan->fsm_state = EICON_STATE_WMCONN; break; } + if (chan->l2prot == ISDN_PROTO_L2_FAX) { +#ifdef CONFIG_ISDN_TTY_FAX + chan->fsm_state = EICON_STATE_ACTIVE; + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + if (chan->fax) { + if (chan->fax->phase == ISDN_FAX_PHASE_B) { + idi_fax_send_header(ccard, chan, 2); + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_DCS; + ccard->interface.statcallb(&cmd); + } + } + else { + if (DebugVar & 1) + printk(KERN_DEBUG "idi_ind: N_CONNECT_ACK with NULL fax struct, ERROR\n"); + } +#endif + break; + } chan->fsm_state = EICON_STATE_ACTIVE; cmd.driver = ccard->myid; cmd.command = ISDN_STAT_BCONN; @@ -1152,6 +1244,9 @@ if (DebugVar & 16) printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No); if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1); + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + break; + } if (chan->l2prot == ISDN_PROTO_L2_MODEM) { chan->fsm_state = EICON_STATE_WMCONN; break; @@ -1169,6 +1264,12 @@ idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1); idi_do_req(ccard, chan, REMOVE, 1); } +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + idi_fax_hangup(ccard, chan); + } +#endif chan->queued = 0; chan->waitq = 0; chan->waitpq = 0; @@ -1182,6 +1283,12 @@ case IDI_N_DISC_ACK: if (DebugVar & 16) printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No); +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + idi_fax_hangup(ccard, chan); + } +#endif break; case IDI_N_DATA_ACK: if (DebugVar & 16) @@ -1191,12 +1298,23 @@ skb_pull(skb, sizeof(eicon_IND) - 1); if (DebugVar & 128) printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len); - ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb); - free_buff = 0; + if (chan->l2prot == ISDN_PROTO_L2_FAX) { +#ifdef CONFIG_ISDN_TTY_FAX + idi_faxdata_rcv(ccard, chan, skb); +#endif + } else { + ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb); + free_buff = 0; + } break; case IDI_N_UDATA: idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); break; +#ifdef CONFIG_ISDN_TTY_FAX + case IDI_N_EDATA: + idi_edata_action(ccard, chan, ind->RBuffer.P, ind->RBuffer.length); + break; +#endif default: if (DebugVar & 8) printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind); @@ -1231,21 +1349,19 @@ /* Remove an Id */ if (chan->e.Req == REMOVE) { - if (ack->Reference == chan->e.ref) { - ccard->IdTable[ack->RcId] = NULL; - if (DebugVar & 16) - printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No, - ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); - if (!chan->e.ReqCh) - chan->e.D3Id = 0; - else - chan->e.B2Id = 0; - } - else { + if (ack->Reference != chan->e.ref) { if (DebugVar & 1) printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No, ack->Reference, chan->e.ref); } + ccard->IdTable[ack->RcId] = NULL; + if (DebugVar & 16) + printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No, + ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig"); + if (!chan->e.ReqCh) + chan->e.D3Id = 0; + else + chan->e.B2Id = 0; return; } @@ -1268,6 +1384,25 @@ ccard->interface.statcallb(&cmd); } chan->waitpq = 0; +#ifdef CONFIG_ISDN_TTY_FAX + if (chan->l2prot == ISDN_PROTO_L2_FAX) { + if (((chan->queued - chan->waitq) < 1) && + (chan->fax2.Eop)) { + chan->fax2.Eop = 0; + if (chan->fax) { + cmd.driver = ccard->myid; + cmd.command = ISDN_STAT_FAXIND; + cmd.arg = chan->No; + chan->fax->r_code = ISDN_TTY_FAX_SENT; + ccard->interface.statcallb(&cmd); + } + else { + if (DebugVar & 1) + printk(KERN_DEBUG "idi_ack: Sent with NULL fax struct, ERROR\n"); + } + } + } +#endif } chan->queued -= chan->waitq; if (chan->queued < 0) chan->queued = 0; @@ -1300,6 +1435,7 @@ if (DebugVar & 1) printk(KERN_ERR "idi_ack: Ch%d: unhandled RC 0x%x\n", dCh, ack->Rc); + break; case READY_INT: case TIMER_INT: /* we do nothing here */ @@ -1317,7 +1453,7 @@ case ASSIGN_OK: if (chan) { if (DebugVar & 1) - printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%d,%d)\n", + printk(KERN_ERR "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n", chan->No, chan->e.D3Id, chan->e.B2Id); } for(j = 0; j < ccard->nchannels + 1; j++) { @@ -1330,7 +1466,7 @@ ccard->bch[j].e.busy = 0; ccard->bch[j].e.ref = 0; if (DebugVar & 16) - printk(KERN_DEBUG"idi_ack: Ch%d: Id %d assigned (%s)\n", j, + printk(KERN_DEBUG"idi_ack: Ch%d: Id %x assigned (%s)\n", j, ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig"); break; } @@ -1407,10 +1543,14 @@ xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC); skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC); - if ((!skb) || (!skb2)) { + if ((!xmit_skb) || (!skb2)) { restore_flags(flags); if (DebugVar & 1) - printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No); + printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No); + if (xmit_skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1462,7 +1602,11 @@ if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err: alloc_skb failed in manage_assign()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1501,7 +1645,11 @@ if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err: alloc_skb failed in manage_remove()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); return -ENOMEM; } @@ -1538,7 +1686,8 @@ chan = &(card->bch[card->nchannels]); - if (chan->e.D3Id) return -EBUSY; + if (chan->e.D3Id) + return -EBUSY; chan->e.D3Id = 1; while((skb2 = skb_dequeue(&chan->e.X))) dev_kfree_skb(skb2); @@ -1568,6 +1717,7 @@ return -ENOMEM; } if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) { + kfree(manbuf); chan->e.D3Id = 0; return -EFAULT; } @@ -1577,7 +1727,11 @@ if ((!skb) || (!skb2)) { if (DebugVar & 1) - printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n"); + printk(KERN_WARNING "idi_err_manif: alloc_skb failed in manage()\n"); + if (skb) + dev_kfree_skb(skb); + if (skb2) + dev_kfree_skb(skb2); kfree(manbuf); chan->e.D3Id = 0; return -ENOMEM; @@ -1623,11 +1777,13 @@ } if ((ret = eicon_idi_manage_remove(card))) { + kfree(manbuf); chan->e.D3Id = 0; return(ret); } if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) { + kfree(manbuf); chan->e.D3Id = 0; return -EFAULT; } diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/eicon/eicon_idi.h linux/drivers/isdn/eicon/eicon_idi.h --- v2.2.11/linux/drivers/isdn/eicon/eicon_idi.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/eicon/eicon_idi.h Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: eicon_idi.h,v 1.5 1999/07/11 17:16:26 armin Exp $ +/* $Id: eicon_idi.h,v 1.6 1999/07/25 15:12:04 armin Exp $ * * ISDN lowlevel-module for the Eicon.Diehl active cards. * IDI-Interface @@ -21,6 +21,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_idi.h,v $ + * Revision 1.6 1999/07/25 15:12:04 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.5 1999/07/11 17:16:26 armin * Bugfixes in queue handling. * Added DSP-DTMF decoder functions. @@ -256,5 +260,9 @@ extern int eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb); extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que); extern void idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value); +#ifdef CONFIG_ISDN_TTY_FAX +extern void idi_fax_cmd(eicon_card *card, eicon_chan *chan); +extern int idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb); +#endif #endif diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/eicon/eicon_io.c linux/drivers/isdn/eicon/eicon_io.c --- v2.2.11/linux/drivers/isdn/eicon/eicon_io.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/eicon/eicon_io.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: eicon_io.c,v 1.1 1999/03/29 11:19:45 armin Exp $ +/* $Id: eicon_io.c,v 1.2 1999/07/25 15:12:05 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Code for communicating with hardware. @@ -24,6 +24,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_io.c,v $ + * Revision 1.2 1999/07/25 15:12:05 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.1 1999/03/29 11:19:45 armin * I/O stuff now in seperate file (eicon_io.c) * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. @@ -57,7 +61,7 @@ break; default: printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%d\n", ind->Ind, ind->IndId); - printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", + printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length); } } @@ -303,6 +307,7 @@ } switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -314,6 +319,7 @@ scom = 0; prram = (eicon_pr_ram *)isa_card->shmem; break; +#endif case EICON_CTYPE_MAESTRAP: scom = 0; ram = (char *)pci_card->PCIram; @@ -434,7 +440,7 @@ chan->e.busy = 1; restore_flags(flags); if (DebugVar & 32) - printk(KERN_DEBUG "eicon: Req=%x Id=%x Ch=%x Len=%x Ref=%d\n", + printk(KERN_DEBUG "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n", reqbuf->Req, ram_inb(ccard, &ReqOut->ReqId), reqbuf->ReqCh, reqbuf->XBuffer.length, @@ -510,6 +516,7 @@ isa_card = &ccard->hwif.isa; switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -523,6 +530,7 @@ prram = (eicon_pr_ram *)isa_card->shmem; irqprobe = &isa_card->irqprobe; break; +#endif case EICON_CTYPE_MAESTRAP: scom = 0; ram = (char *)pci_card->PCIram; @@ -546,6 +554,7 @@ if (*irqprobe) { switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -563,6 +572,7 @@ } (*irqprobe)++; break; +#endif case EICON_CTYPE_MAESTRAP: if (readb(&ram[0x3fe])) { writeb(0, &prram->RcOutput); @@ -581,6 +591,7 @@ } switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: @@ -592,6 +603,7 @@ return; } break; +#endif case EICON_CTYPE_MAESTRAP: if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */ if (DebugVar & 1) @@ -629,7 +641,7 @@ ack->RcCh = ram_inb(ccard, &com->RcCh); ack->Reference = ccard->ref_in++; if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n", + printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", tmp,ack->RcId,ack->RcCh,ack->Reference); skb_queue_tail(&ccard->rackq, skb); eicon_schedule_ack(ccard); @@ -652,7 +664,7 @@ ind->MLength = ram_inw(ccard, &com->MLength); ind->RBuffer.length = len; if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", + printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len); skb_queue_tail(&ccard->rcvq, skb); @@ -679,7 +691,7 @@ ack->RcCh = ram_inb(ccard, &RcIn->RcCh); ack->Reference = ram_inw(ccard, &RcIn->Reference); if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n", + printk(KERN_INFO "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n", Rc,ack->RcId,ack->RcCh,ack->Reference); ram_outb(ccard, &RcIn->Rc, 0); skb_queue_tail(&ccard->rackq, skb); @@ -711,7 +723,7 @@ ind->MLength = ram_inw(ccard, &IndIn->MLength); ind->RBuffer.length = len; if (DebugVar & 64) - printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", + printk(KERN_INFO "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n", Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len); ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len); skb_queue_tail(&ccard->rcvq, skb); @@ -728,6 +740,7 @@ /* clear interrupt */ switch(ccard->type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_QUADRO: writeb(0, isa_card->intack); writeb(0, &com[0x401]); @@ -738,6 +751,7 @@ case EICON_CTYPE_S2M: writeb(0, isa_card->intack); break; +#endif case EICON_CTYPE_MAESTRAP: writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]); writew(0, &cfg[MP_IRQ_RESET + 2]); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/eicon/eicon_isa.c linux/drivers/isdn/eicon/eicon_isa.c --- v2.2.11/linux/drivers/isdn/eicon/eicon_isa.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/eicon/eicon_isa.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: eicon_isa.c,v 1.5 1999/04/01 12:48:33 armin Exp $ +/* $Id: eicon_isa.c,v 1.6 1999/07/25 15:12:06 armin Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Hardware-specific code for old ISA cards. @@ -22,6 +22,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_isa.c,v $ + * Revision 1.6 1999/07/25 15:12:06 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.5 1999/04/01 12:48:33 armin * Changed some log outputs. * @@ -53,7 +57,9 @@ #define release_shmem release_region #define request_shmem request_region -char *eicon_isa_revision = "$Revision: 1.5 $"; +char *eicon_isa_revision = "$Revision: 1.6 $"; + +#ifdef CONFIG_ISDN_DRV_EICON_ISA /* Mask for detecting invalid IRQ parameter */ static int eicon_isa_valid_irq[] = { @@ -430,3 +436,5 @@ card->irqprobe = 0; return 0; } + +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/eicon/eicon_mod.c linux/drivers/isdn/eicon/eicon_mod.c --- v2.2.11/linux/drivers/isdn/eicon/eicon_mod.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/eicon/eicon_mod.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: eicon_mod.c,v 1.7 1999/07/11 17:16:27 armin Exp $ +/* $Id: eicon_mod.c,v 1.8 1999/07/25 15:12:08 armin Exp $ * * ISDN lowlevel-module for Eicon.Diehl active cards. * @@ -26,6 +26,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_mod.c,v $ + * Revision 1.8 1999/07/25 15:12:08 armin + * fix of some debug logs. + * enabled ISA-cards option. + * * Revision 1.7 1999/07/11 17:16:27 armin * Bugfixes in queue handling. * Added DSP-DTMF decoder functions. @@ -73,7 +77,7 @@ static eicon_card *cards = (eicon_card *) NULL; /* glob. var , contains start of card-list */ -static char *eicon_revision = "$Revision: 1.7 $"; +static char *eicon_revision = "$Revision: 1.8 $"; extern char *eicon_pci_revision; extern char *eicon_isa_revision; @@ -88,19 +92,23 @@ ulong DebugVar; /* Parameters to be set by insmod */ +#ifdef CONFIG_ISDN_DRV_EICON_ISA static int membase = -1; static int irq = -1; +#endif static char *id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; MODULE_DESCRIPTION( "Driver for Eicon.Diehl active ISDN cards"); MODULE_AUTHOR( "Armin Schindler"); MODULE_SUPPORTED_DEVICE( "ISDN subsystem"); +MODULE_PARM_DESC(id, "ID-String of first card"); +MODULE_PARM(id, "s"); +#ifdef CONFIG_ISDN_DRV_EICON_ISA MODULE_PARM_DESC(membase, "Base address of first ISA card"); MODULE_PARM_DESC(irq, "IRQ of first card"); -MODULE_PARM_DESC(id, "ID-String of first card"); MODULE_PARM(membase, "i"); MODULE_PARM(irq, "i"); -MODULE_PARM(id, "s"); +#endif char *eicon_ctype_name[] = { "ISDN-S", @@ -382,6 +390,7 @@ card->bus); ret = -ENODEV; } +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_SETMMIO: if (card->flags & EICON_FLAGS_LOADED) return -EBUSY; @@ -401,6 +410,7 @@ card->bus); ret = -ENODEV; } +#endif case EICON_IOCTL_GETIRQ: switch (card->bus) { case EICON_BUS_ISA: @@ -434,6 +444,7 @@ card->bus); ret = -ENODEV; } +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_LOADBOOT: if (card->flags & EICON_FLAGS_RUNNING) return -EBUSY; @@ -452,6 +463,8 @@ ret = -ENODEV; } return ret; +#endif +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_IOCTL_LOADISA: if (card->flags & EICON_FLAGS_RUNNING) return -EBUSY; @@ -484,7 +497,7 @@ ret = -ENODEV; } return ret; - +#endif case EICON_IOCTL_MANIF: if (!card->flags & EICON_FLAGS_RUNNING) return -ENODEV; @@ -813,7 +826,9 @@ int i; int j; int qloop; +#ifdef CONFIG_ISDN_DRV_EICON_ISA char qid[5]; +#endif eicon_card *card; #if CONFIG_PCI eicon_pci_card *pcic; @@ -854,6 +869,7 @@ card->myid = -1; card->type = Type; switch (Type) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA #if CONFIG_MCA /* only needed for MCA */ case EICON_CTYPE_S: case EICON_CTYPE_SX: @@ -913,6 +929,7 @@ card->nchannels = 2; card->interface.channels = 1; break; +#endif #if CONFIG_PCI case EICON_CTYPE_MAESTRA: (eicon_pci_card *)pcic = (eicon_pci_card *)membase; @@ -964,6 +981,7 @@ card->interface.channels = 1; break; #endif +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_CTYPE_ISABRI: if (membase == -1) membase = EICON_ISA_MEMBASE; @@ -992,6 +1010,7 @@ card->nchannels = 30; card->interface.channels = 1; break; +#endif default: printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type); kfree(card); @@ -1029,6 +1048,7 @@ eicon_registercard(eicon_card * card) { switch (card->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: /* TODO something to print */ break; @@ -1037,6 +1057,7 @@ eicon_isa_printpar(&card->hwif.isa); break; #endif +#endif case EICON_BUS_PCI: #if CONFIG_PCI eicon_pci_printpar(&card->hwif.pci); @@ -1070,12 +1091,14 @@ cmd.driver = card->myid; card->interface.statcallb(&cmd); switch (card->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: #ifdef CONFIG_MCA case EICON_BUS_MCA: #endif eicon_isa_release(&card->hwif.isa); break; +#endif case EICON_BUS_PCI: #if CONFIG_PCI eicon_pci_release(&card->hwif.pci); @@ -1107,9 +1130,11 @@ int added = 0; int failed = 0; +#ifdef CONFIG_ISDN_DRV_EICON_ISA if (!Type) /* ISA */ if ((Type = eicon_isa_find_card(membase, irq, id)) < 0) return 0; +#endif eicon_alloccard(Type, membase, irq, id); p = cards; while (p) { @@ -1120,12 +1145,14 @@ */ added++; switch (p->bus) { +#ifdef CONFIG_ISDN_DRV_EICON_ISA case EICON_BUS_ISA: case EICON_BUS_MCA: if (eicon_registercard(p)) break; registered = 1; break; +#endif case EICON_BUS_PCI: #if CONFIG_PCI if (eicon_registercard(p)) @@ -1187,10 +1214,18 @@ printk("%s/", eicon_getrev(tmprev)); release += getrel(tmprev); strcpy(tmprev, eicon_pci_revision); +#if CONFIG_PCI printk("%s/", eicon_getrev(tmprev)); +#else + printk("---/"); +#endif release += getrel(tmprev); strcpy(tmprev, eicon_isa_revision); +#ifdef CONFIG_ISDN_DRV_EICON_ISA printk("%s/", eicon_getrev(tmprev)); +#else + printk("---/"); +#endif release += getrel(tmprev); strcpy(tmprev, eicon_idi_revision); printk("%s\n", eicon_getrev(tmprev)); @@ -1199,6 +1234,7 @@ printk(KERN_INFO "%s Release: %s.%s\n", DRIVERNAME, DRIVERRELEASE, tmprev); +#ifdef CONFIG_ISDN_DRV_EICON_ISA #ifdef CONFIG_MCA /* Check if we have MCA-bus */ if (!MCA_bus) @@ -1216,14 +1252,23 @@ #else card_count = eicon_addcard(0, membase, irq, id); #endif /* CONFIG_MCA */ +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ #if CONFIG_PCI card_count += eicon_pci_find_card(id); #endif if (!cards) { #ifdef MODULE +#ifndef CONFIG_PCI +#ifndef CONFIG_ISDN_DRV_EICON_ISA + printk(KERN_INFO "Eicon: Driver is neither ISA nor PCI compiled !\n"); +#else printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n"); #endif +#else + printk(KERN_INFO "Eicon: No PCI-cards found, driver not loaded !\n"); +#endif +#endif return -ENODEV; } else @@ -1291,7 +1336,7 @@ } #endif /* MODULE */ - +#ifdef CONFIG_ISDN_DRV_EICON_ISA #ifdef CONFIG_MCA struct eicon_mca_adapters_struct { @@ -1457,4 +1502,5 @@ }; }; #endif /* CONFIG_MCA */ +#endif /* CONFIG_ISDN_DRV_EICON_ISA */ diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/eicon/eicon_pci.c linux/drivers/isdn/eicon/eicon_pci.c --- v2.2.11/linux/drivers/isdn/eicon/eicon_pci.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/eicon/eicon_pci.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: eicon_pci.c,v 1.7 1999/06/09 19:31:29 armin Exp $ +/* $Id: eicon_pci.c,v 1.9 1999/08/11 21:01:11 keil Exp $ * * ISDN low-level module for Eicon.Diehl active ISDN-Cards. * Hardware-specific code for PCI cards. @@ -26,6 +26,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: eicon_pci.c,v $ + * Revision 1.9 1999/08/11 21:01:11 keil + * new PCI codefix + * + * Revision 1.8 1999/08/10 16:02:20 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * * Revision 1.7 1999/06/09 19:31:29 armin * Wrong PLX size for request_region() corrected. * Added first MCA code from Erik Weber. @@ -64,7 +70,7 @@ #include "eicon_pci.h" -char *eicon_pci_revision = "$Revision: 1.7 $"; +char *eicon_pci_revision = "$Revision: 1.9 $"; #if CONFIG_PCI /* intire stuff is only for PCI */ @@ -139,8 +145,8 @@ aparms->type = EICON_CTYPE_MAESTRA; aparms->irq = pdev->irq; - preg = pdev->base_address[2] & 0xfffffffc; - pcfg = pdev->base_address[1] & 0xffffff80; + preg = get_pcibase(pdev, 2) & 0xfffffffc; + pcfg = get_pcibase(pdev, 1) & 0xffffff80; #ifdef EICON_PCI_DEBUG printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq); @@ -161,9 +167,9 @@ printk(KERN_INFO "Eicon: DIVA Server PRI/PCI detected !\n"); aparms->type = EICON_CTYPE_MAESTRAP; /*includes 9M,30M*/ aparms->irq = pdev->irq; - pram = pdev->base_address[0] & 0xfffff000; - preg = pdev->base_address[2] & 0xfffff000; - pcfg = pdev->base_address[4] & 0xfffff000; + pram = get_pcibase(pdev, 0) & 0xfffff000; + preg = get_pcibase(pdev, 2) & 0xfffff000; + pcfg = get_pcibase(pdev, 4) & 0xfffff000; #ifdef EICON_PCI_DEBUG printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile --- v2.2.11/linux/drivers/isdn/hisax/Makefile Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/Makefile Wed Aug 25 17:29:47 1999 @@ -197,7 +197,8 @@ include $(TOPDIR)/Rules.make MD5FILES += isac.c isdnl1.c isdnl2.c isdnl3.c \ - tei.c callc.c cert.c l3dss1.c l3_1tr6.c elsa.c + tei.c callc.c cert.c l3dss1.c l3_1tr6.c \ + elsa.c diva.c CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/avm_pci.c linux/drivers/isdn/hisax/avm_pci.c --- v2.2.11/linux/drivers/isdn/hisax/avm_pci.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/avm_pci.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: avm_pci.c,v 1.9 1999/07/12 21:04:57 keil Exp $ +/* $Id: avm_pci.c,v 1.11 1999/08/11 21:01:18 keil Exp $ * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards * Thanks to AVM, Berlin for informations @@ -7,6 +7,12 @@ * * * $Log: avm_pci.c,v $ + * Revision 1.11 1999/08/11 21:01:18 keil + * new PCI codefix + * + * Revision 1.10 1999/08/10 16:01:44 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * * Revision 1.9 1999/07/12 21:04:57 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -50,7 +56,7 @@ #include extern const char *CardType[]; -static const char *avm_pci_rev = "$Revision: 1.9 $"; +static const char *avm_pci_rev = "$Revision: 1.11 $"; #define AVM_FRITZ_PCI 1 #define AVM_FRITZ_PNP 2 @@ -802,7 +808,7 @@ printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n"); return(0); } - cs->hw.avm.cfg_reg = dev_avm->base_address[1] & + cs->hw.avm.cfg_reg = get_pcibase(dev_avm, 1) & PCI_BASE_ADDRESS_IO_MASK; if (!cs->hw.avm.cfg_reg) { printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n"); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/bkm_a4t.c linux/drivers/isdn/hisax/bkm_a4t.c --- v2.2.11/linux/drivers/isdn/hisax/bkm_a4t.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/bkm_a4t.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: bkm_a4t.c,v 1.4 1999/07/14 11:43:14 keil Exp $ +/* $Id: bkm_a4t.c,v 1.6 1999/08/11 21:01:22 keil Exp $ * bkm_a4t.c low level stuff for T-Berkom A4T * derived from the original file sedlbauer.c * derived from the original file niccy.c @@ -7,6 +7,12 @@ * Author Roland Klabunde (R.Klabunde@Berkom.de) * * $Log: bkm_a4t.c,v $ + * Revision 1.6 1999/08/11 21:01:22 keil + * new PCI codefix + * + * Revision 1.5 1999/08/10 16:01:46 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * * Revision 1.4 1999/07/14 11:43:14 keil * correct PCI_SUBSYSTEM_VENDOR_ID * @@ -35,7 +41,7 @@ extern const char *CardType[]; -const char *bkm_a4t_revision = "$Revision: 1.4 $"; +const char *bkm_a4t_revision = "$Revision: 1.6 $"; static inline u_char @@ -314,7 +320,7 @@ &sub_sys_id); if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) { found = 1; - pci_memaddr = dev_a4t->base_address[0]; + pci_memaddr = get_pcibase(dev_a4t, 0); cs->irq = dev_a4t->irq; } } diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/bkm_a8.c linux/drivers/isdn/hisax/bkm_a8.c --- v2.2.11/linux/drivers/isdn/hisax/bkm_a8.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/bkm_a8.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: bkm_a8.c,v 1.4 1999/07/14 11:43:15 keil Exp $ +/* $Id: bkm_a8.c,v 1.6 1999/08/11 21:01:24 keil Exp $ * bkm_a8.c low level stuff for Scitel Quadro (4*S0, passive) * derived from the original file sedlbauer.c * derived from the original file niccy.c @@ -7,6 +7,12 @@ * Author Roland Klabunde (R.Klabunde@Berkom.de) * * $Log: bkm_a8.c,v $ + * Revision 1.6 1999/08/11 21:01:24 keil + * new PCI codefix + * + * Revision 1.5 1999/08/10 16:01:48 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * * Revision 1.4 1999/07/14 11:43:15 keil * correct PCI_SUBSYSTEM_VENDOR_ID * @@ -36,7 +42,7 @@ extern const char *CardType[]; -const char sct_quadro_revision[] = "$Revision: 1.4 $"; +const char sct_quadro_revision[] = "$Revision: 1.6 $"; /* To survive the startup phase */ typedef struct { @@ -371,7 +377,7 @@ &sub_sys_id); if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) { found = 1; - pci_ioaddr1 = dev_a8->base_address[1]; + pci_ioaddr1 = get_pcibase(dev_a8, 1); pci_irq = dev_a8->irq; pci_bus = dev_a8->bus->number; pci_device_fn = dev_a8->devfn; @@ -433,7 +439,7 @@ pcibios_write_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, pci_ioaddr1); #ifdef COMPAT_HAS_NEW_PCI - dev_a8->base_address[1] = pci_ioaddr1; + get_pcibase(dev_a8, 1) = pci_ioaddr1; #endif /* COMPAT_HAS_NEW_PCI */ } /* End HACK */ diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c --- v2.2.11/linux/drivers/isdn/hisax/callc.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/callc.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: callc.c,v 2.29 1999/07/13 21:05:41 werner Exp $ +/* $Id: callc.c,v 2.31 1999/08/05 20:43:10 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,12 @@ * Fritz Elfert * * $Log: callc.c,v $ + * Revision 2.31 1999/08/05 20:43:10 keil + * ISAR analog modem support + * + * Revision 2.30 1999/07/25 16:24:04 keil + * Fixed TEI now working again + * * Revision 2.29 1999/07/13 21:05:41 werner * Modified set_channel_limit to use new callback ISDN_STAT_DISCH. * @@ -133,7 +139,7 @@ #endif /* COMPAT_HAS_NEW_SYMTAB */ #endif /* MODULE */ -const char *lli_revision = "$Revision: 2.29 $"; +const char *lli_revision = "$Revision: 2.31 $"; extern struct IsdnCard cards[]; extern int nrcards; @@ -304,7 +310,7 @@ HL_LL(struct Channel *chanp, int command) { isdn_ctrl ic; - + ic.driver = chanp->cs->myid; ic.command = command; ic.arg = chanp->chan; @@ -430,12 +436,21 @@ lli_go_active(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; + isdn_ctrl ic; + FsmChangeState(fi, ST_ACTIVE); chanp->data_open = !0; + if (chanp->bcs->conmsg) + strcpy(ic.parm.num, chanp->bcs->conmsg); + else + ic.parm.num[0] = 0; if (chanp->debug & 1) - link_debug(chanp, 0, "STAT_BCONN"); - HL_LL(chanp, ISDN_STAT_BCONN); + link_debug(chanp, 0, "STAT_BCONN %s", ic.parm.num); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_BCONN; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan); } @@ -1341,9 +1356,10 @@ st->l1.mode = L1_MODE_TRANS; break; case (ISDN_PROTO_L2_MODEM): - st->l1.mode = L1_MODE_MODEM; + st->l1.mode = L1_MODE_V32; break; } + chanp->bcs->conmsg = NULL; if (chanp->bcs->BC_SetStack(st, chanp->bcs)) return (-1); st->l2.flag = 0; @@ -1747,6 +1763,8 @@ HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num); printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", num); + chanp->d_st->lli.l4l3(chanp->d_st, + DL_ESTABLISH | REQUEST, NULL); break; case (9): /* load firmware */ memcpy(&adr, ic->parm.num, sizeof(ulong)); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/cert.c linux/drivers/isdn/hisax/cert.c --- v2.2.11/linux/drivers/isdn/hisax/cert.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/cert.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: cert.c,v 2.1 1998/11/15 23:51:15 keil Exp $ +/* $Id: cert.c,v 2.2 1999/08/07 17:35:05 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * @@ -7,6 +7,9 @@ * ../../../Documentation/isdn/HiSax.cert * * $Log: cert.c,v $ + * Revision 2.2 1999/08/07 17:35:05 keil + * approval for Eicon Technology Diva 2.01 PCI + * * Revision 2.1 1998/11/15 23:51:15 keil * certification stuff * @@ -29,6 +32,7 @@ printk(KERN_INFO "HiSax: Approval registration numbers:\n"); printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n"); printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n"); + printk(KERN_INFO "HiSax: Approved with Eicon Technology Diva 2.01 PCI cards\n"); } return(0); #endif diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c --- v2.2.11/linux/drivers/isdn/hisax/config.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/config.c Wed Aug 25 17:29:47 1999 @@ -1,10 +1,16 @@ -/* $Id: config.c,v 2.28 1999/07/14 12:38:36 werner Exp $ +/* $Id: config.c,v 2.30 1999/08/05 20:43:14 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * * * $Log: config.c,v $ + * Revision 2.30 1999/08/05 20:43:14 keil + * ISAR analog modem support + * + * Revision 2.29 1999/07/21 14:46:00 keil + * changes from EICON certification + * * Revision 2.28 1999/07/14 12:38:36 werner * Added changes for echo channel handling * @@ -508,9 +514,9 @@ printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n"); #ifdef MODULE - printk(KERN_INFO "HiSax: Version 3.2a (module)\n"); + printk(KERN_INFO "HiSax: Version 3.3 (module)\n"); #else - printk(KERN_INFO "HiSax: Version 3.2a (kernel)\n"); + printk(KERN_INFO "HiSax: Version 3.3 (kernel)\n"); #endif strcpy(tmp, l1_revision); printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp)); @@ -1047,7 +1053,7 @@ cs->iif.features = ISDN_FEATURE_L2_X75I | ISDN_FEATURE_L2_HDLC | -// ISDN_FEATURE_L2_MODEM | + ISDN_FEATURE_L2_MODEM | ISDN_FEATURE_L2_TRANS | ISDN_FEATURE_L3_TRANS | #ifdef CONFIG_HISAX_1TR6 diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/diva.c linux/drivers/isdn/hisax/diva.c --- v2.2.11/linux/drivers/isdn/hisax/diva.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/diva.c Wed Aug 25 17:29:47 1999 @@ -1,13 +1,29 @@ -/* $Id: diva.c,v 1.12 1999/07/12 21:05:04 keil Exp $ +/* $Id: diva.c,v 1.16 1999/08/11 21:01:25 keil Exp $ * diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards * * Author Karsten Keil (keil@isdn4linux.de) * - * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * + * Thanks to Eicon Technology for documents and informations * * * $Log: diva.c,v $ + * Revision 1.16 1999/08/11 21:01:25 keil + * new PCI codefix + * + * Revision 1.15 1999/08/10 16:01:49 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.14 1999/08/07 17:35:08 keil + * approval for Eicon Technology Diva 2.01 PCI + * + * Revision 1.13 1999/07/21 14:46:07 keil + * changes from EICON certification + * * Revision 1.12 1999/07/12 21:05:04 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -64,7 +80,7 @@ extern const char *CardType[]; -const char *Diva_revision = "$Revision: 1.12 $"; +const char *Diva_revision = "$Revision: 1.16 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -891,7 +907,7 @@ val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR, cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID); printk(KERN_INFO "Diva: IPAC version %x\n", val); - if (val == 1) { + if ((val == 1) || (val==2)) { cs->subtyp = DIVA_IPAC_ISA; cs->hw.diva.ctrl = 0; cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA; @@ -922,23 +938,23 @@ PCI_DIVA20_ID, dev_diva))) { cs->subtyp = DIVA_PCI; cs->irq = dev_diva->irq; - cs->hw.diva.cfg_reg = dev_diva->base_address[2] + cs->hw.diva.cfg_reg = get_pcibase(dev_diva, 2) & PCI_BASE_ADDRESS_IO_MASK; } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_EICON_DIEHL, PCI_DIVA20_U_ID, dev_diva_u))) { cs->subtyp = DIVA_PCI; cs->irq = dev_diva_u->irq; - cs->hw.diva.cfg_reg = dev_diva_u->base_address[2] + cs->hw.diva.cfg_reg = get_pcibase(dev_diva_u, 2) & PCI_BASE_ADDRESS_IO_MASK; } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_EICON_DIEHL, PCI_DIVA_201, dev_diva201))) { cs->subtyp = DIVA_IPAC_PCI; cs->irq = dev_diva201->irq; cs->hw.diva.pci_cfg = - (ulong) ioremap((dev_diva201->base_address[0] + (ulong) ioremap((get_pcibase(dev_diva201, 0) & PCI_BASE_ADDRESS_IO_MASK), 4096); cs->hw.diva.cfg_reg = - (ulong) ioremap((dev_diva201->base_address[1] + (ulong) ioremap((get_pcibase(dev_diva201, 1) & PCI_BASE_ADDRESS_IO_MASK), 4096); } else { printk(KERN_WARNING "Diva: No PCI card found\n"); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c --- v2.2.11/linux/drivers/isdn/hisax/elsa.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/elsa.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: elsa.c,v 2.14 1999/07/12 21:05:07 keil Exp $ +/* $Id: elsa.c,v 2.17 1999/08/11 20:57:40 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * @@ -13,8 +13,19 @@ * Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE) * for ELSA PCMCIA support * - * * $Log: elsa.c,v $ + * Revision 2.17 1999/08/11 20:57:40 keil + * bugfix IPAC version 1.1 + * new PCI codefix + * + * Revision 2.16 1999/08/10 16:01:51 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 2.15 1999/08/09 19:25:21 keil + * Support (alpha version) for the '98 model of ELSA Microlink ISDN/MC + * by Christer Weinigel, Cendio Systems AB + * Add support for IPAC 1.2 + * * Revision 2.14 1999/07/12 21:05:07 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -85,10 +96,11 @@ extern const char *CardType[]; -const char *Elsa_revision = "$Revision: 2.14 $"; +const char *Elsa_revision = "$Revision: 2.17 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", - "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"}; + "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI", + "PCMCIA-IPAC" }; const char *ITACVer[] = {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2", @@ -118,6 +130,7 @@ #define ELSA_QS3000 8 #define ELSA_QS1000PCI 9 #define ELSA_QS3000PCI 10 +#define ELSA_PCMCIA_IPAC 11 /* PCI stuff */ #define PCI_VENDOR_ELSA 0x1048 @@ -445,9 +458,11 @@ printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); return; } - val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */ - if (!(val & ELSA_PCI_IRQ_MASK)) - return; + if (cs->subtyp == ELSA_QS1000PCI || cs->subtyp == ELSA_QS3000PCI) { + val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */ + if (!(val & ELSA_PCI_IRQ_MASK)) + return; + } #if ARCOFI_USE if (cs->hw.elsa.MFlag) { val = serial_inp(cs, UART_IIR); @@ -513,6 +528,9 @@ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); release_region(cs->hw.elsa.cfg, 0x80); } + if (cs->subtyp == ELSA_PCMCIA_IPAC) { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); + } if ((cs->subtyp == ELSA_PCFPRO) || (cs->subtyp == ELSA_QS3000) || (cs->subtyp == ELSA_PCF) || @@ -547,7 +565,7 @@ if (cs->hw.elsa.trig) byteout(cs->hw.elsa.trig, 0xff); } - if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) { save_flags(flags); sti(); writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); @@ -558,8 +576,14 @@ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); schedule_timeout((10*HZ)/1000); /* Timeout 10ms */ restore_flags(flags); - writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0); - writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c); + if (cs->subtyp != ELSA_PCMCIA_IPAC) { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c); + } else { + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_PCFG, 0x10); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x4); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0xf8); + } writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); if (cs->subtyp == ELSA_QS1000PCI) byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */ @@ -675,7 +699,7 @@ { int blink = 0; - if (cs->subtyp == ELSA_PCMCIA) + if (cs->subtyp == ELSA_PCMCIA || cs->subtyp == ELSA_PCMCIA_IPAC) return; del_timer(&cs->hw.elsa.tl); if (cs->hw.elsa.status & ELSA_ASSIGN) @@ -739,6 +763,7 @@ return(0); case CARD_TEST: if ((cs->subtyp == ELSA_PCMCIA) || + (cs->subtyp == ELSA_PCMCIA_IPAC) || (cs->subtyp == ELSA_QS1000PCI)) { return(0); } else if (cs->subtyp == ELSA_QS3000PCI) { @@ -989,10 +1014,19 @@ } else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) { cs->hw.elsa.base = card->para[1]; cs->irq = card->para[0]; - cs->subtyp = ELSA_PCMCIA; - cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; - cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; - cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + val = readreg(cs->hw.elsa.base + 0, cs->hw.elsa.base + 2, IPAC_ID); + if ((val == 1) || (val == 2)) { /* IPAC version 1.1/1.2 */ + cs->subtyp = ELSA_PCMCIA_IPAC; + cs->hw.elsa.ale = cs->hw.elsa.base + 0; + cs->hw.elsa.isac = cs->hw.elsa.base + 2; + cs->hw.elsa.hscx = cs->hw.elsa.base + 2; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + cs->subtyp = ELSA_PCMCIA; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + } cs->hw.elsa.timer = 0; cs->hw.elsa.trig = 0; cs->hw.elsa.ctrl = 0; @@ -1013,17 +1047,17 @@ dev_qs1000))) { cs->subtyp = ELSA_QS1000PCI; cs->irq = dev_qs1000->irq; - cs->hw.elsa.cfg = dev_qs1000->base_address[1] & + cs->hw.elsa.cfg = get_pcibase(dev_qs1000, 1) & PCI_BASE_ADDRESS_IO_MASK; - cs->hw.elsa.base = dev_qs1000->base_address[3] & + cs->hw.elsa.base = get_pcibase(dev_qs1000, 3) & PCI_BASE_ADDRESS_IO_MASK; } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ELSA, PCI_QS3000_ID, dev_qs3000))) { cs->subtyp = ELSA_QS3000PCI; cs->irq = dev_qs3000->irq; - cs->hw.elsa.cfg = dev_qs3000->base_address[1] & + cs->hw.elsa.cfg = get_pcibase(dev_qs3000, 1) & PCI_BASE_ADDRESS_IO_MASK; - cs->hw.elsa.base = dev_qs3000->base_address[3] & + cs->hw.elsa.base = get_pcibase(dev_qs3000, 3) & PCI_BASE_ADDRESS_IO_MASK; } else { printk(KERN_WARNING "Elsa: No PCI card found\n"); @@ -1123,6 +1157,7 @@ case ELSA_PCC16: case ELSA_QS1000: case ELSA_PCMCIA: + case ELSA_PCMCIA_IPAC: bytecnt = 8; break; case ELSA_PCFPRO: @@ -1197,7 +1232,7 @@ cs->BC_Send_Data = &hscx_fill_fifo; cs->cardmsg = &Elsa_card_msg; reset_elsa(cs); - if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) { cs->readisac = &ReadISAC_IPAC; cs->writeisac = &WriteISAC_IPAC; cs->readisacfifo = &ReadISACfifo_IPAC; diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/gazel.c linux/drivers/isdn/hisax/gazel.c --- v2.2.11/linux/drivers/isdn/hisax/gazel.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/gazel.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: gazel.c,v 2.3 1999/07/12 21:05:09 keil Exp $ +/* $Id: gazel.c,v 2.5 1999/08/11 21:01:26 keil Exp $ * gazel.c low level stuff for Gazel isdn cards * @@ -6,6 +6,12 @@ * based on source code from Karsten Keil * * $Log: gazel.c,v $ + * Revision 2.5 1999/08/11 21:01:26 keil + * new PCI codefix + * + * Revision 2.4 1999/08/10 16:01:54 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * * Revision 2.3 1999/07/12 21:05:09 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -29,7 +35,7 @@ #endif extern const char *CardType[]; -const char *gazel_revision = "$Revision: 2.3 $"; +const char *gazel_revision = "$Revision: 2.5 $"; #define R647 1 #define R685 2 @@ -588,8 +594,8 @@ if ((dev_tel = pci_find_device(GAZEL_MANUFACTURER, seekcard, dev_tel))) { pci_irq = dev_tel->irq; - pci_ioaddr0 = dev_tel->base_address[1]; - pci_ioaddr1 = dev_tel->base_address[2]; + pci_ioaddr0 = get_pcibase(dev_tel, 1); + pci_ioaddr1 = get_pcibase(dev_tel, 2); found = 1; } #else diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/hfc_pci.c linux/drivers/isdn/hisax/hfc_pci.c --- v2.2.11/linux/drivers/isdn/hisax/hfc_pci.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/hfc_pci.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: hfc_pci.c,v 1.7 1999/07/14 21:24:20 werner Exp $ +/* $Id: hfc_pci.c,v 1.13 1999/08/11 21:01:28 keil Exp $ * hfc_pci.c low level driver for CCD´s hfc-pci based cards * @@ -23,6 +23,25 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: hfc_pci.c,v $ + * Revision 1.13 1999/08/11 21:01:28 keil + * new PCI codefix + * + * Revision 1.12 1999/08/10 16:01:58 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.11 1999/08/09 19:13:32 werner + * moved constant pci ids to pci id table + * + * Revision 1.10 1999/08/08 10:17:34 werner + * added new PCI vendor and card ids for Manufacturer 0x1043 + * + * Revision 1.9 1999/08/07 21:09:10 werner + * Fixed another memcpy problem in fifo handling. + * Thanks for debugging aid by Olaf Kordwittenborg. + * + * Revision 1.8 1999/07/23 14:25:15 werner + * Some smaller bug fixes and prepared support for GCI/IOM bus + * * Revision 1.7 1999/07/14 21:24:20 werner * fixed memcpy problem when using E-channel feature * @@ -60,7 +79,22 @@ extern const char *CardType[]; -static const char *hfcpci_revision = "$Revision: 1.7 $"; +static const char *hfcpci_revision = "$Revision: 1.13 $"; + +static const int CCD_VENDOR_IDS[] = { + 0x1043, /* Asuscom */ + 0x1051, /* Motorola MC145575 */ + 0x1397, /* CCD and Billion */ + 0, +}; + +static const int CCD_DEVICE_IDS[] = { + 0x675, /* Asuscom */ + 0x100, /* Motorola MC145575 */ + 0x2BD0, /* CCD and Billion */ + 0, +}; + #if CONFIG_PCI /*****************************/ @@ -151,6 +185,20 @@ Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); cs->hw.hfcpci.sctrl_r = 0; Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); + + /* Init GCI/IOM2 in master mode */ + /* Slots 0 and 1 are set for B-chan 1 and 2 */ + /* D- and monitor/CI channel are not enabled */ + /* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */ + /* STIO2 is used as data input, B1+B2 from IOM->ST */ + /* ST B-channel send disabled -> continous 1s */ + /* The IOM slots are always enabled */ + cs->hw.hfcpci.conn = 0x36; /* set data flow directions */ + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); + Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */ + Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */ + Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */ + Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */ restore_flags(flags); } @@ -242,7 +290,7 @@ count -= 3; ptr = skb_put(skb, count); - if (zp->z1 >= zp->z2) + if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL) maxlen = count; /* complete transfer */ else maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ @@ -306,7 +354,7 @@ rcnt -= 3; ptr = skb_put(skb, rcnt); - if (zp->z1 >= zp->z2) + if (zp->z2 + rcnt <= D_FIFO_SIZE) maxlen = rcnt; /* complete transfer */ else maxlen = D_FIFO_SIZE - zp->z2; /* maximum */ @@ -570,9 +618,13 @@ /* set/reset echo mode */ /***********************/ int hfcpci_set_echo(struct IsdnCardState *cs, int i) -{ +{ int flags; + if (cs->chanlimit > 1) return(-EINVAL); + + save_flags(flags); + cli(); if (i) { cs->logecho = 1; cs->hw.hfcpci.trm |= 0x20; /* enable echo chan */ @@ -587,7 +639,7 @@ } cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA; cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; - cs->hw.hfcpci.conn &= ~0x18; + cs->hw.hfcpci.conn |= 0x10; /* B2-IOM -> B2-ST */ cs->hw.hfcpci.ctmt &= ~2; Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r); @@ -596,6 +648,7 @@ Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm); Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + restore_flags(flags); return(0); } /* hfcpci_set_echo */ @@ -654,7 +707,7 @@ rcnt -= 3; ptr = e_buffer; - if (zp->z1 >= zp->z2) + if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL) maxlen = rcnt; /* complete transfer */ else maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ @@ -1091,7 +1144,7 @@ cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); } else { cs->hw.hfcpci.ctmt |= 1; - cs->hw.hfcpci.conn &= ~0x3; + cs->hw.hfcpci.conn &= ~0x03; cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; @@ -1115,6 +1168,21 @@ cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); } break; + case (L1_MODE_EXTRN): + if (bc) { + cs->hw.hfcpci.conn |= 0x10; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS+HFCPCI_INTS_B2REC); + } else { + cs->hw.hfcpci.conn |= 0x02; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1; + cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS+HFCPCI_INTS_B1REC); + } + break; } Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); restore_flags(flags); @@ -1384,6 +1452,10 @@ { struct IsdnCardState *cs = card->cs; char tmp[64]; + int i; +#ifdef COMPAT_HAS_NEW_PCI + struct pci_dev *tmp_hfcpci = NULL; +#endif strcpy(tmp, hfcpci_revision); printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); @@ -1402,8 +1474,17 @@ printk(KERN_ERR "HFC-PCI: no PCI bus present\n"); return (0); } - if ((dev_hfcpci = pci_find_device(PCI_VENDOR_CCD, - PCI_CCD_PCI_ID, dev_hfcpci))) { + i = 0; + while (CCD_VENDOR_IDS[i]) { + tmp_hfcpci = pci_find_device(CCD_VENDOR_IDS[i], + CCD_DEVICE_IDS[i], + dev_hfcpci); + if (tmp_hfcpci) break; + i++; + } + + if (tmp_hfcpci) { + dev_hfcpci = tmp_hfcpci; /* old device */ cs->hw.hfcpci.pci_bus = dev_hfcpci->bus->number; cs->hw.hfcpci.pci_device_fn = dev_hfcpci->devfn; cs->irq = dev_hfcpci->irq; @@ -1411,8 +1492,7 @@ printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); return (0); } - cs->hw.hfcpci.pci_io = (char *) - dev_hfcpci->base_address[1]; + cs->hw.hfcpci.pci_io = (char *) get_pcibase(dev_hfcpci, 1); } else { printk(KERN_WARNING "HFC-PCI: No PCI card found\n"); return (0); @@ -1421,11 +1501,17 @@ for (; pci_index < 255; pci_index++) { unsigned char irq; - if (pcibios_find_device(PCI_VENDOR_CCD, - PCI_CCD_PCI_ID, pci_index, - &cs->hw.hfcpci.pci_bus, &cs->hw.hfcpci.pci_device_fn) != 0) { - continue; + i = 0; + while (CCD_VENDOR_IDS[i]) { + if (pcibios_find_device(CCD_VENDOR_IDS[i], + CCD_DEVICE_IDS[i], pci_index, + &cs->hw.hfcpci.pci_bus, &cs->hw.hfcpci.pci_device_fn) == 0) + break; + i++; } + if (!CCD_VENDOR_IDS[i]) + continue; + pcibios_read_config_byte(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_INTERRUPT_LINE, &irq); cs->irq = irq; diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/hfc_pci.h linux/drivers/isdn/hisax/hfc_pci.h --- v2.2.11/linux/drivers/isdn/hisax/hfc_pci.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/hfc_pci.h Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: hfc_pci.h,v 1.3 1999/07/14 12:39:34 werner Exp $ +/* $Id: hfc_pci.h,v 1.5 1999/08/09 19:13:34 werner Exp $ * specific defines for CCD's HFC 2BDS0 PCI chips * @@ -21,6 +21,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: hfc_pci.h,v $ + * Revision 1.5 1999/08/09 19:13:34 werner + * moved constant pci ids to pci id table + * + * Revision 1.4 1999/08/08 10:17:33 werner + * added new PCI vendor and card ids for Manufacturer 0x1043 + * * Revision 1.3 1999/07/14 12:39:34 werner * Added changes for echo handling. * @@ -34,10 +40,6 @@ /* defines for PCI config */ -#define PCI_VENDOR_CCD 0x1397 -#define PCI_CCD_PCI_ID 0x2BD0 -// #define PCI_VENDOR_CCD 0x1043 -// #define PCI_CCD_PCI_ID 0x675 #define PCI_ENA_MEMIO 0x02 #define PCI_ENA_MASTER 0x04 diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/hfcscard.c linux/drivers/isdn/hisax/hfcscard.c --- v2.2.11/linux/drivers/isdn/hisax/hfcscard.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/hfcscard.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: hfcscard.c,v 1.3 1999/07/12 21:05:12 keil Exp $ +/* $Id: hfcscard.c,v 1.4 1999/08/09 18:59:59 keil Exp $ * hfcscard.c low level stuff for hfcs based cards (Teles3c, ACER P10) * @@ -6,6 +6,9 @@ * * * $Log: hfcscard.c,v $ + * Revision 1.4 1999/08/09 18:59:59 keil + * Fix S0 init - Thanks to Stefan Gybas + * * Revision 1.3 1999/07/12 21:05:12 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -24,7 +27,7 @@ extern const char *CardType[]; -static const char *hfcs_revision = "$Revision: 1.3 $"; +static const char *hfcs_revision = "$Revision: 1.4 $"; static void hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs) @@ -105,8 +108,8 @@ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */ udelay(10); cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */ - cs->hw.hfcD.mst_m = 0; - cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, HFCD_MASTER); /* HFC Master */ + cs->hw.hfcD.mst_m = HFCD_MASTER; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); /* HFC Master */ cs->hw.hfcD.sctrl = 0; cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl); restore_flags(flags); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h --- v2.2.11/linux/drivers/isdn/hisax/hisax.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/hisax.h Wed Aug 25 17:29:47 1999 @@ -1,8 +1,14 @@ -/* $Id: hisax.h,v 2.30 1999/07/14 12:38:38 werner Exp $ +/* $Id: hisax.h,v 2.33 1999/08/05 20:43:16 keil Exp $ * Basic declarations, defines and prototypes * * $Log: hisax.h,v $ + * Revision 2.33 1999/08/05 20:43:16 keil + * ISAR analog modem support + * + * Revision 2.31 1999/07/21 14:46:11 keil + * changes from EICON certification + * * Revision 2.30 1999/07/14 12:38:38 werner * Added changes for echo channel handling * @@ -321,6 +327,7 @@ #define FLG_ESTAB_PEND 13 #define FLG_PTP 14 #define FLG_FIXED_TEI 15 +#define FLG_L2BLOCK 16 struct Layer2 { int tei; @@ -452,6 +459,7 @@ int txcnt; int mml; u_char *rcvbuf; /* B-Channel receive Buffer */ + u_char conmsg[16]; struct isar_reg *reg; }; @@ -519,11 +527,15 @@ #define BC_FLG_NOFRAME 4 #define BC_FLG_HALF 5 #define BC_FLG_EMPTY 6 +#define BC_FLG_ORIG 7 #define L1_MODE_NULL 0 #define L1_MODE_TRANS 1 #define L1_MODE_HDLC 2 +#define L1_MODE_EXTRN 3 #define L1_MODE_MODEM 7 +#define L1_MODE_V32 8 +#define L1_MODE_FAX 9 struct BCState { int channel; @@ -536,6 +548,7 @@ struct sk_buff_head squeue; /* B-Channel send Queue */ struct PStack *st; u_char *blog; + u_char *conmsg; struct timer_list transbusy; struct tq_struct tqueue; int event; diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/isac.c linux/drivers/isdn/hisax/isac.c --- v2.2.11/linux/drivers/isdn/hisax/isac.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/isac.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: isac.c,v 1.21 1999/07/12 21:05:17 keil Exp $ +/* $Id: isac.c,v 1.22 1999/08/09 19:04:40 keil Exp $ * isac.c ISAC specific routines * @@ -9,6 +9,9 @@ * ../../../Documentation/isdn/HiSax.cert * * $Log: isac.c,v $ + * Revision 1.22 1999/08/09 19:04:40 keil + * Fix race condition - Thanks to Christer Weinigel + * * Revision 1.21 1999/07/12 21:05:17 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -233,7 +236,6 @@ cs->tx_cnt += count; cs->writeisacfifo(cs, ptr, count); cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { debugl1(cs, "isac_fill_fifo dbusytimer running"); del_timer(&cs->dbusytimer); @@ -241,6 +243,7 @@ init_timer(&cs->dbusytimer); cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); add_timer(&cs->dbusytimer); + restore_flags(flags); if (cs->debug & L1_DEB_ISAC_FIFO) { char *t = cs->dlog; diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c --- v2.2.11/linux/drivers/isdn/hisax/isar.c Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/isar.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: isar.c,v 1.3 1999/07/01 08:11:45 keil Exp $ +/* $Id: isar.c,v 1.4 1999/08/05 20:43:18 keil Exp $ * isar.c ISAR (Siemens PSB 7110) specific routines * @@ -6,6 +6,9 @@ * * * $Log: isar.c,v $ + * Revision 1.4 1999/08/05 20:43:18 keil + * ISAR analog modem support + * * Revision 1.3 1999/07/01 08:11:45 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * @@ -428,6 +431,7 @@ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); break; case L1_MODE_TRANS: + case L1_MODE_V32: if ((skb = dev_alloc_skb(ireg->clsb))) { SET_SKB_FREE(skb); rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb)); @@ -517,6 +521,7 @@ printk(KERN_ERR"isar_fill_fifo wrong mode 0\n"); break; case L1_MODE_TRANS: + case L1_MODE_V32: if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, 0, count, ptr)) { if (cs->debug) @@ -598,6 +603,136 @@ } } +const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", + "300", "600", "1200", "2400", "4800", "7200", + "9600nt", "9600t", "12000", "14400", "WRONG"}; +const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21", + "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"}; + +static void +isar_pump_status_rsp(struct BCState *bcs, struct isar_reg *ireg) { + struct IsdnCardState *cs = bcs->cs; + u_char ril = ireg->par[0]; + u_char rim; + + if (!test_and_clear_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags)) + return; + if (ril > 14) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "wrong pstrsp ril=%d",ril); + ril = 15; + } + switch(ireg->par[1]) { + case 0: + rim = 0; + break; + case 0x20: + rim = 2; + break; + case 0x40: + rim = 3; + break; + case 0x41: + rim = 4; + break; + case 0x51: + rim = 5; + break; + case 0x61: + rim = 6; + break; + case 0x71: + rim = 7; + break; + case 0x82: + rim = 8; + break; + case 0x92: + rim = 9; + break; + case 0xa2: + rim = 10; + break; + default: + rim = 1; + break; + } + sprintf(bcs->hw.isar.conmsg,"%s %s", dmril[ril], dmrim[rim]); + bcs->conmsg = bcs->hw.isar.conmsg; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump strsp %s", bcs->conmsg); +} + +static void +isar_pump_status_ev(struct BCState *bcs, u_char devt) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + + switch(devt) { + case PSEV_10MS_TIMER: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev TIMER"); + break; + case PSEV_CON_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CONNECT"); + l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL); + break; + case PSEV_CON_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev NO CONNECT"); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + l1_msg_b(bcs->st, PH_DEACTIVATE | REQUEST, NULL); + break; + case PSEV_V24_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev V24 OFF"); + break; + case PSEV_CTS_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CTS ON"); + break; + case PSEV_CTS_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CTS OFF"); + break; + case PSEV_DCD_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CARRIER ON"); + test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags); + sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL); + break; + case PSEV_DCD_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev CARRIER OFF"); + break; + case PSEV_DSR_ON: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev DSR ON"); +// sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, 0xCF, 0, NULL); + break; + case PSEV_DSR_OFF: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev DSR_OFF"); + break; + case PSEV_REM_RET: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev REMOTE RETRAIN"); + break; + case PSEV_REM_REN: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev REMOTE RENEGOTIATE"); + break; + case PSEV_GSTN_CLR: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "pump stev GSTN CLEAR", devt); + break; + default: + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "unknown pump stev %x", devt); + break; + } +} static char debbuf[64]; @@ -633,10 +768,31 @@ if (cs->debug & L1_DEB_WARN) debugl1(cs, "Buffer STEV dpath%d msb(%x)", ireg->iis>>6, ireg->cmsb); + case ISAR_IIS_PSTEV: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + rcv_mbox(cs, ireg, (u_char *)ireg->par); + isar_pump_status_ev(bcs, ireg->cmsb); + } else { + debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar spurious IIS_PSTEV %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } break; - case ISAR_IIS_DIAG: case ISAR_IIS_PSTRSP: - case ISAR_IIS_PSTEV: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + rcv_mbox(cs, ireg, (u_char *)ireg->par); + isar_pump_status_rsp(bcs, ireg); + } else { + debugl1(cs, "isar spurious IIS_PSTRSP %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar spurious IIS_PSTRSP %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_DIAG: case ISAR_IIS_BSTRSP: case ISAR_IIS_IOM2RSP: rcv_mbox(cs, ireg, (u_char *)ireg->par); @@ -664,7 +820,8 @@ setup_pump(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); - + u_char ctrl, param[6]; + switch (bcs->mode) { case L1_MODE_NULL: case L1_MODE_TRANS: @@ -675,6 +832,44 @@ bcs->hw.isar.dpath); } break; + case L1_MODE_V32: + ctrl = PMOD_DATAMODEM; + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + ctrl |= PCTRL_ORIG; + param[5] = PV32P6_CTN; + } else { + param[5] = PV32P6_ATN; + } + param[0] = 11; /* 11 db */ +// param[1] = PV32P2_V22A | PV32P2_V22B | PV32P2_V21; + param[1] = PV32P2_V22A; +// param[2] = PV32P3_AMOD | PV32P3_V32B; + param[2] = PV32P3_AMOD; + param[3] = PV32P4_48; + param[4] = PV32P5_48; +// param[3] = PV32P4_UT144; +// param[4] = PV32P5_UT144; + if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param)) { + if (cs->debug) + debugl1(cs, "isar pump datamodem cfg dp%d failed", + bcs->hw.isar.dpath); + } + break; + case L1_MODE_FAX: + ctrl = PMOD_FAX; + if (test_bit(BC_FLG_ORIG, &bcs->Flag)) { + ctrl |= PCTRL_ORIG; + param[1] = PFAXP2_CTN; + } else { + param[1] = PFAXP2_ATN; + } + param[0] = 8; /* 8 db */ + if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param)) { + if (cs->debug) + debugl1(cs, "isar pump faxmodem cfg dp%d failed", + bcs->hw.isar.dpath); + } + break; } if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) { if (cs->debug) @@ -687,6 +882,7 @@ setup_sart(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); + u_char ctrl, param[2]; switch (bcs->mode) { case L1_MODE_NULL: @@ -706,7 +902,17 @@ case L1_MODE_HDLC: if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, "\0")) { if (cs->debug) - debugl1(cs, "isar sart binary dp%d failed", + debugl1(cs, "isar sart hdlc dp%d failed", + bcs->hw.isar.dpath); + } + break; + case L1_MODE_V32: + ctrl = SMODE_V14 | SCTRL_HDMC_BOTH; + param[0] = S_P1_CHS_8; + param[1] = S_P2_BFT_DEF; + if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2, param)) { + if (cs->debug) + debugl1(cs, "isar sart v14 dp%d failed", bcs->hw.isar.dpath); } break; @@ -722,18 +928,22 @@ setup_iom2(struct BCState *bcs) { struct IsdnCardState *cs = bcs->cs; u_char dps = SET_DPS(bcs->hw.isar.dpath); - u_char cmsb = 0, msg[5] = {0x10,0,0,0,0}; + u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD,0,0,0,0}; + if (bcs->channel) + msg[1] = msg[3] = 1; switch (bcs->mode) { case L1_MODE_NULL: + cmsb = 0; /* dummy slot */ msg[1] = msg[3] = bcs->hw.isar.dpath + 2; break; case L1_MODE_TRANS: case L1_MODE_HDLC: - cmsb = 0x80; - if (bcs->channel) - msg[1] = msg[3] = 1; + break; + case L1_MODE_V32: + case L1_MODE_FAX: + cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV; break; } if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) { @@ -768,7 +978,18 @@ &bcs->hw.isar.reg->Flags)) bcs->hw.isar.dpath = 1; else { - printk(KERN_ERR"isar modeisar both pathes in use\n"); + printk(KERN_WARNING"isar modeisar both pathes in use\n"); + return(1); + } + break; + case L1_MODE_V32: + /* only datapath 1 */ + if (!test_and_set_bit(ISAR_DP1_USE, + &bcs->hw.isar.reg->Flags)) + bcs->hw.isar.dpath = 1; + else { + printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n"); + debugl1(cs, "isar modeisar analog funktions only with DP1"); return(1); } break; @@ -779,8 +1000,8 @@ bcs->hw.isar.dpath, bcs->mode, mode, bc); bcs->mode = mode; setup_pump(bcs); - setup_sart(bcs); setup_iom2(bcs); + setup_sart(bcs); if (bcs->mode == L1_MODE_NULL) { /* Clear resources */ if (bcs->hw.isar.dpath == 1) @@ -858,8 +1079,24 @@ break; case (PH_ACTIVATE | REQUEST): test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); - modeisar(st->l1.bcs, st->l1.mode, st->l1.bc); - l1_msg_b(st, pr, arg); + st->l1.bcs->hw.isar.conmsg[0] = 0; + if (test_bit(FLG_ORIG, &st->l2.flag)) + test_and_set_bit(BC_FLG_ORIG, &st->l1.bcs->Flag); + else + test_and_clear_bit(BC_FLG_ORIG, &st->l1.bcs->Flag); + switch(st->l1.mode) { + case L1_MODE_TRANS: + case L1_MODE_HDLC: + if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc)) + l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); + else + l1_msg_b(st, PH_ACTIVATE | REQUEST, arg); + break; + case L1_MODE_V32: + if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc)) + l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg); + break; + } break; case (PH_DEACTIVATE | REQUEST): l1_msg_b(st, pr, arg); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/isar.h linux/drivers/isdn/hisax/isar.h --- v2.2.11/linux/drivers/isdn/hisax/isar.h Mon Aug 9 16:05:55 1999 +++ linux/drivers/isdn/hisax/isar.h Wed Aug 25 17:29:47 1999 @@ -1,10 +1,13 @@ -/* $Id: isar.h,v 1.3 1999/07/01 08:11:46 keil Exp $ +/* $Id: isar.h,v 1.4 1999/08/05 20:43:20 keil Exp $ * isar.h ISAR (Siemens PSB 7110) specific defines * * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: isar.h,v $ + * Revision 1.4 1999/08/05 20:43:20 keil + * ISAR analog modem support + * * Revision 1.3 1999/07/01 08:11:46 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * @@ -39,6 +42,7 @@ #define ISAR_HIS_P12CFG 0x24 #define ISAR_HIS_SARTCFG 0x25 #define ISAR_HIS_PUMPCFG 0x26 +#define ISAR_HIS_PUMPCTRL 0x2a #define ISAR_HIS_IOM2CFG 0x27 #define ISAR_HIS_IOM2REQ 0x07 #define ISAR_HIS_IOM2CTRL 0x2b @@ -70,12 +74,97 @@ #define ISAR_DP1_USE 1 #define ISAR_DP2_USE 2 +#define ISAR_RATE_REQ 3 +#define PMOD_DISABLE 0 +#define PMOD_FAX 1 +#define PMOD_DATAMODEM 2 +#define PMOD_HALFDUPLEX 3 +#define PMOD_V110 4 +#define PMOD_DTMF 5 +#define PMOD_DTMF_TRANS 6 #define PMOD_BYPASS 7 +#define PCTRL_ORIG 0x80 +#define PV32P2_V23R 0x40 +#define PV32P2_V22A 0x20 +#define PV32P2_V22B 0x10 +#define PV32P2_V22C 0x08 +#define PV32P2_V21 0x02 +#define PV32P2_BEL 0x01 + +#define PV32P3_AMOD 0x80 +#define PV32P3_V32B 0x02 +#define PV32P4_48 0x05 +#define PV32P5_48 0x11 +#define PV32P4_UT48 0x0d +#define PV32P5_UT48 0x11 +#define PV32P4_96 0x03 +#define PV32P5_96 0x11 +#define PV32P4_UT96 0x0f +#define PV32P5_UT96 0x11 +#define PV32P4_B96 0x0b +#define PV32P5_B96 0x91 +#define PV32P4_UTB96 0x0f +#define PV32P5_UTB96 0xd1 +#define PV32P4_120 0x09 +#define PV32P5_120 0xb1 +#define PV32P4_UT120 0x0f +#define PV32P5_UT120 0xf1 +#define PV32P4_144 0x09 +#define PV32P5_144 0x99 +#define PV32P4_UT144 0x0f +#define PV32P5_UT144 0xf9 +#define PV32P6_CTN 0x01 +#define PV32P6_ATN 0x02 +#define PFAXP2_CTN 0x01 +#define PFAXP2_ATN 0x04 + +#define PSEV_10MS_TIMER 0x02 +#define PSEV_CON_ON 0x18 +#define PSEV_CON_OFF 0x19 +#define PSEV_V24_OFF 0x20 +#define PSEV_CTS_ON 0x21 +#define PSEV_CTS_OFF 0x22 +#define PSEV_DCD_ON 0x23 +#define PSEV_DCD_OFF 0x24 +#define PSEV_DSR_ON 0x25 +#define PSEV_DSR_OFF 0x26 +#define PSEV_REM_RET 0xcc +#define PSEV_REM_REN 0xcd +#define PSEV_GSTN_CLR 0xd4 + +#define PCTRL_LOC_RET 0xcf +#define PCTRL_LOC_REN 0xce + #define SMODE_DISABLE 0 +#define SMODE_V14 2 #define SMODE_HDLC 3 #define SMODE_BINARY 4 +#define SMODE_FSK_V14 5 + +#define SCTRL_HDMC_BOTH 0x00 +#define SCTRL_HDMC_DTX 0x80 +#define SCTRL_HDMC_DRX 0x40 +#define S_P1_OVSP 0x40 +#define S_P1_SNP 0x20 +#define S_P1_EOP 0x10 +#define S_P1_EDP 0x08 +#define S_P1_NSB 0x04 +#define S_P1_CHS_8 0x03 +#define S_P1_CHS_7 0x02 +#define S_P1_CHS_6 0x01 +#define S_P1_CHS_5 0x00 + +#define S_P2_BFT_DEF 30 + +#define IOM_CTRL_ENA 0x80 +#define IOM_CTRL_NOPCM 0x00 +#define IOM_CTRL_ALAW 0x02 +#define IOM_CTRL_ULAW 0x04 +#define IOM_CTRL_RCV 0x01 + +#define IOM_P1_TXD 0x10 #define HDLC_FED 0x40 #define HDLC_FSD 0x20 diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c --- v2.2.11/linux/drivers/isdn/hisax/isdnl2.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/isdnl2.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: isdnl2.c,v 2.17 1999/07/01 08:11:50 keil Exp $ +/* $Id: isdnl2.c,v 2.19 1999/08/05 20:40:26 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,12 @@ * Fritz Elfert * * $Log: isdnl2.c,v $ + * Revision 2.19 1999/08/05 20:40:26 keil + * Fix interlayer communication + * + * Revision 2.18 1999/07/21 14:46:16 keil + * changes from EICON certification + * * Revision 2.17 1999/07/01 08:11:50 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * @@ -74,7 +80,7 @@ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 2.17 $"; +const char *l2_revision = "$Revision: 2.19 $"; static void l2m_debug(struct FsmInst *fi, char *fmt, ...); @@ -163,6 +169,19 @@ static int l2addrsize(struct Layer2 *l2); static void +set_peer_busy(struct Layer2 *l2) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) + test_and_set_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +clear_peer_busy(struct Layer2 *l2) { + if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) + test_and_clear_bit(FLG_L2BLOCK, &l2->flag); +} + +static void InitWin(struct Layer2 *l2) { int i; @@ -219,7 +238,7 @@ test_and_clear_bit(FLG_ACK_PEND, &l2->flag); test_and_clear_bit(FLG_REJEXC, &l2->flag); test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); - test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + clear_peer_busy(l2); } inline int @@ -1026,10 +1045,10 @@ skb_pull(skb, l2addrsize(l2)); if (IsRNR(skb->data, st)) { - test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + set_peer_busy(l2); typ = RNR; } else - test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + clear_peer_busy(l2); if (IsREJ(skb->data, st)) typ = REJ; @@ -1376,10 +1395,10 @@ skb_pull(skb, l2addrsize(l2)); if (IsRNR(skb->data, st)) { - test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + set_peer_busy(l2); rnr = 1; } else - test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + clear_peer_busy(l2); if (test_bit(FLG_MOD128, &l2->flag)) { PollFlag = (skb->data[1] & 0x1) == 0x1; @@ -1496,11 +1515,14 @@ } static void -l2_discard_i(struct FsmInst *fi, int event, void *arg) +l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); } static void @@ -1663,9 +1685,10 @@ {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, - {ST_L2_4, EV_L1_DEACTIVATE, l2_discard_i}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c --- v2.2.11/linux/drivers/isdn/hisax/isdnl3.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/isdnl3.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: isdnl3.c,v 2.9 1999/07/01 08:11:53 keil Exp $ +/* $Id: isdnl3.c,v 2.10 1999/07/21 14:46:19 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,9 @@ * Fritz Elfert * * $Log: isdnl3.c,v $ + * Revision 2.10 1999/07/21 14:46:19 keil + * changes from EICON certification + * * Revision 2.9 1999/07/01 08:11:53 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * @@ -65,7 +68,7 @@ #include "isdnl3.h" #include -const char *l3_revision = "$Revision: 2.9 $"; +const char *l3_revision = "$Revision: 2.10 $"; static struct Fsm l3fsm = @@ -347,8 +350,18 @@ if (pp) pp->next = np->next; else if (!(p->st->l3.proc = np->next) && - !test_bit(FLG_PTP, &p->st->l2.flag)) - FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL); + !test_bit(FLG_PTP, &p->st->l2.flag)) { + if (p->debug) + l3_debug(p->st, "release_l3_process: last process"); + if (!skb_queue_len(&p->st->l3.squeue)) { + if (p->debug) + l3_debug(p->st, "release_l3_process: release link"); + FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL); + } else { + if (p->debug) + l3_debug(p->st, "release_l3_process: not release link"); + } + } kfree(p); return; } @@ -362,12 +375,12 @@ static void l3ml3p(struct PStack *st, int pr) { - struct l3_process *p = st->l3.proc; + struct l3_process *p = st->l3.proc; - while (p) { - st->l3.l3ml3(st, pr, p); - p = p->next; - } + while (p) { + st->l3.l3ml3(st, pr, p); + p = p->next; + } } void @@ -474,12 +487,19 @@ { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; + int dequeued = 0; FsmChangeState(fi, ST_L3_LC_ESTAB); while ((skb = skb_dequeue(&st->l3.squeue))) { st->l3.l3l2(st, DL_DATA | REQUEST, skb); + dequeued++; } - l3ml3p(st, DL_ESTABLISH | INDICATION); + if ((!st->l3.proc) && dequeued) { + if (st->l3.debug) + l3_debug(st, "lc_connect: release link"); + FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); + } else + l3ml3p(st, DL_ESTABLISH | INDICATION); } static void @@ -487,13 +507,20 @@ { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; + int dequeued = 0; FsmDelTimer(&st->l3.l3m_timer, 51); FsmChangeState(fi, ST_L3_LC_ESTAB); while ((skb = skb_dequeue(&st->l3.squeue))) { st->l3.l3l2(st, DL_DATA | REQUEST, skb); + dequeued++; } - l3ml3p(st, DL_ESTABLISH | CONFIRM); + if ((!st->l3.proc) && dequeued) { + if (st->l3.debug) + l3_debug(st, "lc_connected: release link"); + FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); + } else + l3ml3p(st, DL_ESTABLISH | CONFIRM); } static void @@ -510,8 +537,15 @@ { struct PStack *st = fi->userdata; - FsmChangeState(fi, ST_L3_LC_REL_WAIT); - st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); + if (test_bit(FLG_L2BLOCK, &st->l2.flag)) { + if (st->l3.debug) + l3_debug(st, "lc_release_req: l2 blocked"); + /* restart release timer */ + FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51); + } else { + FsmChangeState(fi, ST_L3_LC_REL_WAIT); + st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); + } } static void @@ -528,11 +562,11 @@ static void lc_release_cnf(struct FsmInst *fi, int event, void *arg) { - struct PStack *st = fi->userdata; + struct PStack *st = fi->userdata; - FsmChangeState(fi, ST_L3_LC_REL); - discard_queue(&st->l3.squeue); - l3ml3p(st, DL_RELEASE | CONFIRM); + FsmChangeState(fi, ST_L3_LC_REL); + discard_queue(&st->l3.squeue); + l3ml3p(st, DL_RELEASE | CONFIRM); } diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/isdnl3.h linux/drivers/isdn/hisax/isdnl3.h --- v2.2.11/linux/drivers/isdn/hisax/isdnl3.h Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/isdnl3.h Wed Aug 25 17:29:47 1999 @@ -1,6 +1,9 @@ -/* $Id: isdnl3.h,v 2.4 1999/07/01 08:11:54 keil Exp $ +/* $Id: isdnl3.h,v 2.5 1999/07/25 16:18:32 keil Exp $ * $Log: isdnl3.h,v $ + * Revision 2.5 1999/07/25 16:18:32 keil + * Fix Suspend/Resume + * * Revision 2.4 1999/07/01 08:11:54 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * @@ -34,7 +37,7 @@ */ #define SBIT(state) (1< extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 2.14 $"; +const char *dss1_revision = "$Revision: 2.18 $"; #define EXT_BEARER_CAPS 1 @@ -662,10 +671,8 @@ static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, -1}; static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_USER_USER, -1}; -/* not used - * static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, - * IE_CALLED_PN, -1}; - */ +static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, + IE_CALLED_PN, -1}; static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1}; static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS | IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1}; @@ -679,7 +686,8 @@ static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER | IE_MANDATORY, IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_CALLING_PN, - IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_LLC, IE_USER_USER, -1}; + IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_LLC, IE_HLC, + IE_USER_USER, -1}; static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY, IE_PROGRESS, IE_DISPLAY, -1}; static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE | @@ -805,12 +813,13 @@ err_ureg++; } ie = *p++; - if (newpos >= 0) { + if (ie & 0x80) { + l = 1; + } else { l = *p++; p += l; l += 2; - } else - l = 1; + } if (l > getmax_ie_len(ie)) err_len++; } @@ -1243,9 +1252,8 @@ break; case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption - - return DecodeSyncParams(176, p[5]); // V.120 - + if (p[1] > 3) + return DecodeSyncParams(176, p[5]); // V.120 break; } } @@ -1479,8 +1487,11 @@ pc->para.bchannel = id; } else if (1 == pc->state) { if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup answer without chid (ret %d)", id); - pc->para.cause = 96; + l3_debug(pc->st, "setup answer wrong chid (ret %d)", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; l3dss1_status_send(pc, pr, NULL); return; } @@ -1515,8 +1526,11 @@ pc->para.bchannel = id; } else { if (pc->debug & L3_DEB_WARN) - l3_debug(pc->st, "setup answer without chid (ret %d)", id); - pc->para.cause = 96; + l3_debug(pc->st, "setup answer wrong chid (ret %d)", id); + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; l3dss1_status_send(pc, pr, NULL); return; } @@ -1640,10 +1654,6 @@ /* JIM, 05.11.97 I wanna set service indicator 2 */ #if EXT_BEARER_CAPS pc->para.setup.si2 = DecodeSI2(skb); - if (pc->debug & L3_DEB_SI) - l3_debug(pc->st, "SI=%d, AI=%d", - pc->para.setup.si1, - pc->para.setup.si2); #endif break; case 0x09: /* Restricted digital information */ @@ -1664,6 +1674,7 @@ } switch (p[3] & 0x7f) { case 0x40: /* packed mode */ + pc->para.setup.si1 = 8; break; case 0x10: /* 64 kbit */ case 0x11: /* 2*64 kbit */ @@ -1677,6 +1688,9 @@ break; } } + if (pc->debug & L3_DEB_SI) + l3_debug(pc->st, "SI=%d, AI=%d", + pc->para.setup.si1, pc->para.setup.si2); if (err) { if (pc->debug & L3_DEB_WARN) l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)", @@ -1715,7 +1729,10 @@ } else { if (pc->debug & L3_DEB_WARN) l3_debug(pc->st, "setup with wrong chid ret %d", id); - pc->para.cause = 96; + if (id == -1) + pc->para.cause = 96; + else + pc->para.cause = 100; l3dss1_msg_without_setup(pc, pr, NULL); return; } @@ -1991,7 +2008,7 @@ u_char *p; if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) { - if (p[1] != 4) { + if (p[1] != 2) { err = 1; pc->para.cause = 100; } else if (p[2] & 0x60) { @@ -2089,10 +2106,20 @@ ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY); l3dss1_std_ie_err(pc, ret); +// KKe 19.7.99 test eicon +// idev_kfree_skb(skb, FREE_READ); + pc->para.cause = 30; /* response to STATUS_ENQUIRY */ + l3dss1_status_send(pc, pr, NULL); +} - idev_kfree_skb(skb, FREE_READ); +static void +l3dss1_information(struct l3_process *pc, u_char pr, void *arg) +{ + int ret; + struct sk_buff *skb = arg; - l3dss1_status_send(pc, 0x1E, NULL); /* answer status enquire */ + ret = check_infoelements(pc, skb, ie_INFORMATION); + l3dss1_std_ie_err(pc, ret); } /******************************/ @@ -2326,6 +2353,7 @@ l3dss1_setup_req(pc, pr, arg); } else { L3DelTimer(&pc->timer); + l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102); pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc); dss1_release_l3_process(pc); } @@ -2610,7 +2638,7 @@ memcpy(skb_put(skb, l), tmp, l); l3_msg(pc->st, DL_DATA | REQUEST, skb); newl3state(pc, 17); - L3AddTimer(&pc->timer, T319, CC_T319); + L3AddTimer(&pc->timer, T318, CC_T318); } static void @@ -2758,9 +2786,10 @@ static void l3dss1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&pc->timer); + L3DelTimer(&pc->timer); - l3dss1_status_send(pc, 0x1F, NULL); /* normal, unspecified */ + pc->para.cause = 0x1F; /* normal, unspecified */ + l3dss1_status_send(pc, 0, NULL); } /* *INDENT-OFF* */ @@ -2831,8 +2860,10 @@ MT_STATUS, l3dss1_release_ind}, {ALL_STATES, MT_STATUS, l3dss1_status}, - {SBIT(0) | SBIT(6), + {SBIT(0), MT_SETUP, l3dss1_setup}, + {SBIT(6) | SBIT(7), + MT_SETUP, l3dss1_dummy}, {SBIT(1) | SBIT(2), MT_CALL_PROCEEDING, l3dss1_call_proc}, {SBIT(1), @@ -2841,15 +2872,18 @@ MT_ALERTING, l3dss1_alerting}, {SBIT(2) | SBIT(3), MT_PROGRESS, l3dss1_progress}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), + MT_INFORMATION, l3dss1_information}, {SBIT(10) | SBIT(11) | SBIT(15), MT_NOTIFY, l3dss1_notify}, {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | - SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19), + SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) /* | SBIT(17) | SBIT(19)*/, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25), MT_RELEASE, l3dss1_release}, {SBIT(19), MT_RELEASE, l3dss1_release_ind}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15), + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25), MT_DISCONNECT, l3dss1_disconnect}, // {SBIT(11), // MT_DISCONNECT, l3dss1_release_req}, @@ -3021,6 +3055,13 @@ */ if (mt == MT_SETUP) { /* Setup creates a new transaction process */ + if (skb->data[2] & 0x80) { + /* Setup with wrong CREF flag */ + if (st->l3.debug & L3_DEB_STATE) + l3_debug(st, "dss1up wrong CRef flag"); + idev_kfree_skb(skb, FREE_READ); + return; + } if (!(proc = dss1_new_l3_process(st, cr))) { /* May be to answer with RELEASE_COMPLETE and * CAUSE 0x2f "Resource unavailable", but this @@ -3222,8 +3263,3 @@ strcpy(tmp, dss1_revision); printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp)); } - - - - - diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/md5sums.asc linux/drivers/isdn/hisax/md5sums.asc --- v2.2.11/linux/drivers/isdn/hisax/md5sums.asc Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/md5sums.asc Wed Aug 25 17:29:47 1999 @@ -2,28 +2,30 @@ # This are valid md5sums for certificated HiSax driver. # The certification is valid only if the md5sums of all files match. -# The certification is valid only for ELSA QuickStep cards in the moment. +# The certification is valid only for ELSA QuickStep cards and +# Eicon Technology Diva 2.01 PCI cards in the moment. # Read ../../../Documentation/isdn/HiSax.cert for more informations. # -7b86f7c9709d1a96f2591b76db297116 isac.c -773fb8b13e20ef92c0bc991fea23c673 isdnl1.c -f2ef2bc94883f818d0beb184688786bc isdnl2.c -c7b9992f966c645e0eea548b0d3655a0 isdnl3.c -df3c2d2bc312af0689ed97a1fe7ad2df tei.c -94facb2403188ddfb6b83a890a5e7fa0 callc.c -f3ec2a634f06074d16167aaba02b6dc1 cert.c -e69680e894417b00949473ce38259e2a l3dss1.c -04082bae9726b7c34adb008d3752ac73 l3_1tr6.c -6705bf924beeb147d80dc91fa0aa25ee elsa.c +d93f31e02c1b153ec04d16f69e5688b3 isac.c +e2a78c07f32c8ca7c88fc9f92a87dfab isdnl1.c +54490c4f46a998ff4ef34287bc262185 isdnl2.c +f4184a50e35e5b568608e6cb7a693319 isdnl3.c +ef70f4269fdc2ca15100f9b776afaa0d tei.c +cf3923304983e9d64cf35bc5a3533f6c callc.c +bf9605b36429898f7be6630034e83230 cert.c +309261e4c36d950db6978440e8bc8342 l3dss1.c +b674eee9314a7cc413971c84003cf1d2 l3_1tr6.c +8f86d92f43ecc42f6457773168cfc114 elsa.c +24cda374da44b57f6a1bb215424267b5 diva.c # end of md5sums -----BEGIN PGP SIGNATURE----- Version: 2.6.3i Charset: noconv -iQCVAwUBN3FBezpxHvX/mS9tAQGs9wQAk4pSCWvx5CMheSRedQ7nTtIoVQKPiAEx -6/0DWE5hu5IsSOG4ZbLG/ISdad4OOZjWfMpeeIwsHVGVSspGvo9lpIMOS9EqEvr8 -I+kCrPQwKaEN675U2m0ofsELAPOyH2JICjKdbW+iipWI+6oqGta7aw/tbgDqykVr -vz2L4uxmgUY= -=r0Yz +iQCVAwUBN7HnvDpxHvX/mS9tAQFANgP+LGuG98lvCv97vN2dc6T/6hZTxFW+WirJ +XMhU5NHoZ+8MISMOVKB7ugsGO9cJI0lUA0sOe8jtPCo5070nF1ZkNsxV/x7WK2dS +RwXfHy6+TAH7qIiBnkP9odB2lib+VFl/nnkkTwsXfVwRCD8bLaagMPv+nAveDoNE +uff0xxXEnJw= +=Wu2M -----END PGP SIGNATURE----- diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c --- v2.2.11/linux/drivers/isdn/hisax/netjet.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/netjet.c Wed Aug 25 17:29:47 1999 @@ -1,4 +1,4 @@ -/* $Id: netjet.c,v 1.10 1999/07/12 21:05:22 keil Exp $ +/* $Id: netjet.c,v 1.13 1999/08/11 21:01:31 keil Exp $ * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards * @@ -6,8 +6,18 @@ * * Thanks to Traverse Technologie Australia for documents and informations * - * * $Log: netjet.c,v $ + * Revision 1.13 1999/08/11 21:01:31 keil + * new PCI codefix + * + * Revision 1.12 1999/08/10 16:02:00 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.11 1999/08/07 17:32:00 keil + * Asymetric buffers for improved ping times. Interframe spacing + * fix for NJ<->NJ thoughput. Matt Henderson - www.traverse.com.au + * + * * Revision 1.10 1999/07/12 21:05:22 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -65,7 +75,7 @@ extern const char *CardType[]; -const char *NETjet_revision = "$Revision: 1.10 $"; +const char *NETjet_revision = "$Revision: 1.13 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -101,7 +111,8 @@ #define NETJET_IRQM0_WRITE_1 0x01 #define NETJET_IRQM0_WRITE_2 0x02 -#define NETJET_DMA_SIZE 512 +#define NETJET_DMA_TXSIZE 512 +#define NETJET_DMA_RXSIZE 128 #define HDLC_ZERO_SEARCH 0 #define HDLC_FLAG_SEARCH 1 @@ -229,7 +240,7 @@ switch (mode) { case (L1_MODE_NULL): fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, bc, 0xff); + NETJET_DMA_TXSIZE, bc, 0xff); if (cs->debug & L1_DEB_HSCX) debugl1(cs, "Tiger stat rec %d/%d send %d", bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err, @@ -246,7 +257,7 @@ break; case (L1_MODE_HDLC): fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, bc, 0xff); + NETJET_DMA_TXSIZE, bc, 0xff); bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH; bcs->hw.tiger.r_tot = 0; bcs->hw.tiger.r_bitcnt = 0; @@ -255,14 +266,14 @@ bcs->hw.tiger.s_tot = 0; if (! cs->hw.njet.dmactrl) { fill_mem(bcs, bcs->hw.tiger.send, - NETJET_DMA_SIZE, !bc, 0xff); + NETJET_DMA_TXSIZE, !bc, 0xff); cs->hw.njet.dmactrl = 1; byteout(cs->hw.njet.base + NETJET_DMACTRL, cs->hw.njet.dmactrl); byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x3f); } bcs->hw.tiger.sendp = bcs->hw.tiger.send; - bcs->hw.tiger.free = NETJET_DMA_SIZE; + bcs->hw.tiger.free = NETJET_DMA_TXSIZE; test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); break; } @@ -381,6 +392,7 @@ s_val |= 0x80; } bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + bcs->hw.tiger.sendbuf[s_cnt++] = 0xff; // NJ<->NJ thoughput bug fix } bcs->hw.tiger.sendcnt = s_cnt; bcs->tx_cnt -= bcs->tx_skb->len; @@ -412,7 +424,7 @@ int i; register u_char j; register u_char val; - u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -1; + u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_RXSIZE -1; register u_char state = bcs->hw.tiger.r_state; register u_char r_one = bcs->hw.tiger.r_one; register u_char r_val = bcs->hw.tiger.r_val; @@ -567,7 +579,7 @@ static void read_tiger(struct IsdnCardState *cs) { u_int *p; - int cnt = NETJET_DMA_SIZE/2; + int cnt = NETJET_DMA_RXSIZE/2; if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) { debugl1(cs,"tiger warn read double dma %x/%x", @@ -578,7 +590,7 @@ cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ); } if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1) - p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1; + p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1; else p = cs->bcs[0].hw.tiger.rec + cnt - 1; if (cs->bcs[0].mode == L1_MODE_HDLC) @@ -635,12 +647,12 @@ cnt = bcs->hw.tiger.s_end - p; if (cnt < 2) { p = bcs->hw.tiger.send + 1; - cnt = NETJET_DMA_SIZE/2 - 2; + cnt = NETJET_DMA_TXSIZE/2 - 2; } else { p++; p++; - if (cnt <= (NETJET_DMA_SIZE/2)) - cnt += NETJET_DMA_SIZE/2; + if (cnt <= (NETJET_DMA_TXSIZE/2)) + cnt += NETJET_DMA_TXSIZE/2; cnt--; cnt--; } @@ -698,7 +710,7 @@ } test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); bcs->hw.tiger.free = cnt - s_cnt; - if (bcs->hw.tiger.free > (NETJET_DMA_SIZE/2)) + if (bcs->hw.tiger.free > (NETJET_DMA_TXSIZE/2)) test_and_set_bit(BC_FLG_HALF, &bcs->Flag); else { test_and_clear_bit(BC_FLG_HALF, &bcs->Flag); @@ -738,7 +750,7 @@ } static void write_tiger(struct IsdnCardState *cs) { - u_int *p, cnt = NETJET_DMA_SIZE/2; + u_int *p, cnt = NETJET_DMA_TXSIZE/2; if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) { debugl1(cs,"tiger warn write double dma %x/%x", @@ -749,7 +761,7 @@ cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE); } if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE_1) - p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + p = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1; else p = cs->bcs[0].hw.tiger.send + cnt - 1; if (cs->bcs[0].mode == L1_MODE_HDLC) @@ -880,42 +892,42 @@ __initfunc(void inittiger(struct IsdnCardState *cs)) { - if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_TXSIZE * sizeof(unsigned int), GFP_KERNEL | GFP_DMA))) { printk(KERN_WARNING "HiSax: No memory for tiger.send\n"); return; } - cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE/2 - 1; - cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1; + cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE/2 - 1; + cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1; cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send; cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq; cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end; - memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int)); + memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_TXSIZE * sizeof(unsigned int)); debugl1(cs, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send, - (u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1)); + (u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1)); outl(virt_to_bus(cs->bcs[0].hw.tiger.send), cs->hw.njet.base + NETJET_DMA_READ_START); outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq), cs->hw.njet.base + NETJET_DMA_READ_IRQ); outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end), cs->hw.njet.base + NETJET_DMA_READ_END); - if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int), + if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_RXSIZE * sizeof(unsigned int), GFP_KERNEL | GFP_DMA))) { printk(KERN_WARNING "HiSax: No memory for tiger.rec\n"); return; } debugl1(cs, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec, - (u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1)); + (u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1)); cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec; - memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int)); + memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_RXSIZE * sizeof(unsigned int)); outl(virt_to_bus(cs->bcs[0].hw.tiger.rec), cs->hw.njet.base + NETJET_DMA_WRITE_START); - outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE/2 - 1), + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE/2 - 1), cs->hw.njet.base + NETJET_DMA_WRITE_IRQ); - outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1), + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1), cs->hw.njet.base + NETJET_DMA_WRITE_END); debugl1(cs, "tiger: dmacfg %x/%x pulse=%d", inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), @@ -1098,7 +1110,7 @@ printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n"); return(0); } - cs->hw.njet.base = dev_netjet->base_address[0] + cs->hw.njet.base = get_pcibase(dev_netjet, 0) & PCI_BASE_ADDRESS_IO_MASK; if (!cs->hw.njet.base) { printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/niccy.c linux/drivers/isdn/hisax/niccy.c --- v2.2.11/linux/drivers/isdn/hisax/niccy.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/niccy.c Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: niccy.c,v 1.6 1999/07/12 21:05:23 keil Exp $ +/* $Id: niccy.c,v 1.8 1999/08/11 21:01:33 keil Exp $ * niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and * compatible (SAGEM cybermodem) @@ -8,6 +8,12 @@ * Thanks to Dr. Neuhaus and SAGEM for informations * * $Log: niccy.c,v $ + * Revision 1.8 1999/08/11 21:01:33 keil + * new PCI codefix + * + * Revision 1.7 1999/08/10 16:02:04 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * * Revision 1.6 1999/07/12 21:05:23 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -39,7 +45,7 @@ #endif extern const char *CardType[]; -const char *niccy_revision = "$Revision: 1.6 $"; +const char *niccy_revision = "$Revision: 1.8 $"; #define byteout(addr,val) outb(val,addr) #define bytein(addr) inb(addr) @@ -314,16 +320,16 @@ return(0); } cs->irq = niccy_dev->irq; - if (!niccy_dev->base_address[0]) { + if (!get_pcibase(niccy_dev, 0)) { printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); return(0); } - cs->hw.niccy.cfg_reg = niccy_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; - if (!niccy_dev->base_address[1]) { + cs->hw.niccy.cfg_reg = get_pcibase(niccy_dev, 0) & PCI_BASE_ADDRESS_IO_MASK; + if (!get_pcibase(niccy_dev, 1)) { printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); return(0); } - pci_ioaddr = niccy_dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr = get_pcibase(niccy_dev, 1) & PCI_BASE_ADDRESS_IO_MASK; cs->subtyp = NICCY_PCI; } else { printk(KERN_WARNING "Niccy: No PCI card found\n"); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/sedlbauer.c linux/drivers/isdn/hisax/sedlbauer.c --- v2.2.11/linux/drivers/isdn/hisax/sedlbauer.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/sedlbauer.c Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: sedlbauer.c,v 1.11 1999/07/12 21:05:27 keil Exp $ +/* $Id: sedlbauer.c,v 1.14 1999/08/11 20:59:22 keil Exp $ * sedlbauer.c low level stuff for Sedlbauer cards * includes support for the Sedlbauer speed star (speed star II), @@ -17,6 +17,16 @@ * Edgar Toernig * * $Log: sedlbauer.c,v $ + * Revision 1.14 1999/08/11 20:59:22 keil + * new PCI codefix + * fix IRQ problem while unload + * + * Revision 1.13 1999/08/10 16:02:08 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * + * Revision 1.12 1999/08/05 20:43:22 keil + * ISAR analog modem support + * * Revision 1.11 1999/07/12 21:05:27 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -89,7 +99,7 @@ extern const char *CardType[]; -const char *Sedlbauer_revision = "$Revision: 1.11 $"; +const char *Sedlbauer_revision = "$Revision: 1.14 $"; const char *Sedlbauer_Types[] = {"None", "speed card/win", "speed star", "speed fax+", @@ -433,9 +443,11 @@ void release_io_sedlbauer(struct IsdnCardState *cs) { - int bytecnt = (cs->subtyp == SEDL_SPEED_FAX) ? 16 : 8; + int bytecnt = 8; - if (cs->hw.sedl.bus == SEDL_BUS_PCI) { + if (cs->subtyp == SEDL_SPEED_FAX) { + bytecnt = 16; + } else if (cs->hw.sedl.bus == SEDL_BUS_PCI) { bytecnt = 256; } if (cs->hw.sedl.cfg_reg) @@ -488,6 +500,17 @@ reset_sedlbauer(cs); return(0); case CARD_RELEASE: + if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, + ISAR_IRQBIT, 0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, + ISAC_MASK, 0xFF); + reset_sedlbauer(cs); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, + ISAR_IRQBIT, 0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, + ISAC_MASK, 0xFF); + } release_io_sedlbauer(cs); return(0); case CARD_INIT: @@ -576,7 +599,7 @@ printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n"); return(0); } - cs->hw.sedl.cfg_reg = dev_sedl->base_address[0] & + cs->hw.sedl.cfg_reg = get_pcibase(dev_sedl, 0) & PCI_BASE_ADDRESS_IO_MASK; } else { printk(KERN_WARNING "Sedlbauer: No PCI card found\n"); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c --- v2.2.11/linux/drivers/isdn/hisax/tei.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/tei.c Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: tei.c,v 2.12 1999/07/01 08:12:11 keil Exp $ +/* $Id: tei.c,v 2.13 1999/07/21 14:46:28 keil Exp $ * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden @@ -11,6 +11,9 @@ * Fritz Elfert * * $Log: tei.c,v $ + * Revision 2.13 1999/07/21 14:46:28 keil + * changes from EICON certification + * * Revision 2.12 1999/07/01 08:12:11 keil * Common HiSax version for 2.0, 2.1, 2.2 and 2.3 kernel * @@ -75,7 +78,7 @@ #include "isdnl2.h" #include -const char *tei_revision = "$Revision: 2.12 $"; +const char *tei_revision = "$Revision: 2.13 $"; #define ID_REQUEST 1 #define ID_ASSIGNED 2 @@ -230,6 +233,25 @@ } static void +tei_id_test_dup(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *ost, *st = fi->userdata; + struct sk_buff *skb = arg; + int tei, ri; + + ri = ((unsigned int) skb->data[1] << 8) + skb->data[2]; + tei = skb->data[4] >> 1; + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "foreign identity assign ri %d tei %d", ri, tei); + if ((ost = findtei(st, tei))) { /* same tei is in use */ + st->ma.tei_m.printdebug(&st->ma.tei_m, + "possible duplicate assignment tei %d", tei); + FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL); + } +} + +static void tei_id_denied(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; @@ -472,6 +494,7 @@ static struct FsmNode TeiFnList[] HISAX_INITDATA = { {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/hisax/telespci.c linux/drivers/isdn/hisax/telespci.c --- v2.2.11/linux/drivers/isdn/hisax/telespci.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/hisax/telespci.c Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: telespci.c,v 2.7 1999/07/12 21:05:34 keil Exp $ +/* $Id: telespci.c,v 2.9 1999/08/11 21:01:34 keil Exp $ * telespci.c low level stuff for Teles PCI isdn cards * @@ -7,6 +7,12 @@ * * * $Log: telespci.c,v $ + * Revision 2.9 1999/08/11 21:01:34 keil + * new PCI codefix + * + * Revision 2.8 1999/08/10 16:02:10 calle + * struct pci_dev changed in 2.3.13. Made the necessary changes. + * * Revision 2.7 1999/07/12 21:05:34 keil * fix race in IRQ handling * added watchdog for lost IRQs @@ -41,7 +47,7 @@ #endif extern const char *CardType[]; -const char *telespci_revision = "$Revision: 2.7 $"; +const char *telespci_revision = "$Revision: 2.9 $"; #define ZORAN_PO_RQ_PEN 0x02000000 #define ZORAN_PO_WR 0x00800000 @@ -331,10 +337,10 @@ printk(KERN_WARNING "Teles: No IRQ for PCI card found\n"); return(0); } - cs->hw.teles0.membase = (u_int) ioremap(dev_tel->base_address[0], + cs->hw.teles0.membase = (u_int) ioremap(get_pcibase(dev_tel, 0), PAGE_SIZE); printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n", - dev_tel->base_address[0], dev_tel->irq); + get_pcibase(dev_tel, 0), dev_tel->irq); } else { printk(KERN_WARNING "TelesPCI: No PCI card found\n"); return(0); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c --- v2.2.11/linux/drivers/isdn/isdn_audio.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/isdn_audio.c Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: isdn_audio.c,v 1.14 1999/07/11 17:14:06 armin Exp $ +/* $Id: isdn_audio.c,v 1.16 1999/08/06 12:47:35 calle Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * @@ -21,6 +21,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.c,v $ + * Revision 1.16 1999/08/06 12:47:35 calle + * Using __GNUC__ == 2 && __GNUC_MINOR__ < 95 how to define + * ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT + * + * Revision 1.15 1999/08/06 12:02:52 calle + * egcs 2.95 complain about invalid asm statement: + * "fixed or forbidden register 2 (cx) was spilled for class CREG." + * Using ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT and not + * define it at the moment. + * * Revision 1.14 1999/07/11 17:14:06 armin * Added new layer 2 and 3 protocols for Fax and DSP functions. * Moved "Add CPN to RING message" to new register S23, @@ -82,7 +92,7 @@ #include "isdn_audio.h" #include "isdn_common.h" -char *isdn_audio_revision = "$Revision: 1.14 $"; +char *isdn_audio_revision = "$Revision: 1.16 $"; /* * Misc. lookup-tables. @@ -279,7 +289,18 @@ {'*', '0', '#', 'D'} }; -#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) + +/* + * egcs 2.95 complain about invalid asm statement: + * "fixed or forbidden register 2 (cx) was spilled for class CREG." + */ +#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) || defined(__GNUC__) +#if __GNUC__ == 2 && __GNUC_MINOR__ < 95 +#define ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT +#endif +#endif + +#ifdef ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT static inline void isdn_audio_tlookup(const void *table, void *buff, unsigned long n) { diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c --- v2.2.11/linux/drivers/isdn/isdn_cards.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/isdn_cards.c Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: isdn_cards.c,v 1.9 1999/04/12 12:33:11 fritz Exp $ +/* $Id: isdn_cards.c,v 1.10 1999/07/20 06:41:28 calle Exp $ * Linux ISDN subsystem, initialization for non-modularized drivers. * @@ -19,6 +19,10 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.c,v $ + * Revision 1.10 1999/07/20 06:41:28 calle + * Bugfix: After the redesign of the AVM B1 driver, the driver didn't even + * compile, if not selected as modules. + * * Revision 1.9 1999/04/12 12:33:11 fritz * Changes from 2.0 tree. * @@ -67,12 +71,9 @@ #endif #ifdef CONFIG_ISDN_DRV_AVMB1 -extern void avmb1_init(void); +extern void kcapi_init(void); extern void capi_init(void); extern void capidrv_init(void); -#ifdef CONFIG_PCI -extern int b1pci_init(void); -#endif #endif void @@ -88,10 +89,7 @@ pcbit_init(); #endif #ifdef CONFIG_ISDN_DRV_AVMB1 - avmb1_init(); -#ifdef CONFIG_PCI - b1pci_init(); -#endif + kcapi_init(); capi_init(); capidrv_init(); #endif diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c --- v2.2.11/linux/drivers/isdn/isdn_common.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/isdn_common.c Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: isdn_common.c,v 1.83 1999/07/13 21:02:05 werner Exp $ +/* $Id: isdn_common.c,v 1.86 1999/07/31 12:59:42 armin Exp $ * Linux ISDN subsystem, common used functions (linklevel). * @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.86 1999/07/31 12:59:42 armin + * Added tty fax capabilities. + * + * Revision 1.85 1999/07/29 16:58:35 armin + * Bugfix: DLE handling in isdn_readbchan() + * + * Revision 1.84 1999/07/25 16:21:10 keil + * fix number matching + * * Revision 1.83 1999/07/13 21:02:05 werner * Added limit possibilty of driver b_channel resources (ISDN_STAT_DISCH) * @@ -372,7 +381,7 @@ isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.83 $"; +static char *isdn_revision = "$Revision: 1.86 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -483,6 +492,8 @@ register int reverse; register int nostar = 1; + if (!(*s) && !(*p)) + return(1); for (; *p; s++, p++) switch (*p) { case '\\': @@ -1041,6 +1052,11 @@ break; case CAPI_PUT_MESSAGE: return(isdn_capi_rec_hl_msg(&c->parm.cmsg)); +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_STAT_FAXIND: + isdn_tty_stat_callback(i, c); + break; +#endif #ifdef CONFIG_ISDN_AUDIO case ISDN_STAT_AUDIO: isdn_tty_stat_callback(i, c); @@ -1119,7 +1135,7 @@ if (ISDN_AUDIO_SKB_LOCK(skb)) break; ISDN_AUDIO_SKB_LOCK(skb) = 1; - if (ISDN_AUDIO_SKB_DLECOUNT(skb)) { + if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) { char *p = skb->data; unsigned long DLEmask = (1 << channel); @@ -2605,6 +2621,9 @@ for (i = 0; i < ISDN_MAX_CHANNELS; i++) { isdn_tty_cleanup_xmit(&dev->mdm.info[i]); kfree(dev->mdm.info[i].xmit_buf - 4); +#ifdef CONFIG_ISDN_TTY_FAX + kfree(dev->mdm.info[i].fax); +#endif } if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) { printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n"); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c --- v2.2.11/linux/drivers/isdn/isdn_ppp.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/isdn_ppp.c Wed Aug 25 17:29:48 1999 @@ -1352,7 +1352,7 @@ { struct sk_buff *skb_old = skb; int pkt_len; - skb = dev_alloc_skb(skb_old->len + 40); + skb = dev_alloc_skb(skb_old->len + 128); if (!skb) { printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); @@ -1361,7 +1361,7 @@ return; } skb->dev = dev; - skb_put(skb, skb_old->len + 40); + skb_put(skb, skb_old->len + 128); memcpy(skb->data, skb_old->data, skb_old->len); skb->mac.raw = skb->data; pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp, @@ -1413,16 +1413,22 @@ static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) { struct sk_buff *skb = *skb_p; - + if(skb_headroom(skb) < len) { - printk(KERN_ERR "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len); + struct sk_buff *nskb = skb_realloc_headroom(skb, len); + + if (!nskb) { + printk(KERN_INFO "isdn_ppp_skb_push: can't realloc headroom!\n"); + dev_kfree_skb(skb); + return NULL; + } dev_kfree_skb(skb); - return NULL; + *skb_p = nskb; + return skb_push(nskb, len); } return skb_push(skb,len); } - - + /* * send ppp frame .. we expect a PIDCOMPressable proto -- * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP) diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c --- v2.2.11/linux/drivers/isdn/isdn_tty.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/isdn_tty.c Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.c,v 1.68 1999/07/11 17:51:51 armin Exp $ +/* $Id: isdn_tty.c,v 1.72 1999/07/31 12:59:45 armin Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * @@ -20,6 +20,20 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.72 1999/07/31 12:59:45 armin + * Added tty fax capabilities. + * + * Revision 1.71 1999/07/27 10:34:34 armin + * Fixed last change. Did not compile with AUDIO support off. + * + * Revision 1.70 1999/07/25 16:17:58 keil + * Fix Suspend/Resume + * + * Revision 1.69 1999/07/25 12:56:15 armin + * isdn_tty_at_cout() now queues the message if online and + * data is in queue or flip buffer is full. + * needed for fax connections. + * * Revision 1.68 1999/07/11 17:51:51 armin * Bugfix, "-" was missing for AT&L settings. * @@ -331,7 +345,7 @@ static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.68 $"; +char *isdn_tty_revision = "$Revision: 1.72 $"; /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() @@ -534,6 +548,15 @@ ISDN_AUDIO_SKB_DLECOUNT(skb) = isdn_tty_countDLE(skb->data, skb->len); } +#ifdef CONFIG_ISDN_TTY_FAX + else { + if (info->faxonline & 2) { + isdn_tty_fax_bitorder(info, skb); + ISDN_AUDIO_SKB_DLECOUNT(skb) = + isdn_tty_countDLE(skb->data, skb->len); + } + } +#endif #endif /* Try to deliver directly via tty-flip-buf if queue is empty */ save_flags(flags); @@ -863,6 +886,35 @@ } } +/* + * return the usage calculated by si and layer 2 protocol + */ +int +isdn_calc_usage(int si, int l2) +{ + int usg = ISDN_USAGE_MODEM; + +#ifdef CONFIG_ISDN_AUDIO + if (si == 1) { + switch(l2) { + case ISDN_PROTO_L2_MODEM: + usg = ISDN_USAGE_MODEM; + break; +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_PROTO_L2_FAX: + usg = ISDN_USAGE_FAX; + break; +#endif + case ISDN_PROTO_L2_TRANS: + default: + usg = ISDN_USAGE_VOICE; + break; + } + } +#endif + return(usg); +} + /* isdn_tty_dial() performs dialing of a tty an the necessary * setup of the lower levels before that. */ @@ -882,8 +934,14 @@ si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -921,6 +979,12 @@ cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); +#ifdef CONFIG_ISDN_TTY_FAX + if (l2 == ISDN_PROTO_L2_FAX) { + cmd.parm.fax = info->fax; + info->fax->direction = ISDN_TTY_FAX_CONN_OUT; + } +#endif isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; @@ -943,7 +1007,7 @@ * ISDN-line (hangup). The usage-status is cleared * and some cleanup is done also. */ -static void +void isdn_tty_modem_hup(modem_info * info, int local) { isdn_ctrl cmd; @@ -964,6 +1028,10 @@ } #ifdef CONFIG_ISDN_AUDIO info->vonline = 0; +#ifdef CONFIG_ISDN_TTY_FAX + info->faxonline = 0; + info->fax->phase = ISDN_FAX_PHASE_IDLE; +#endif info->emu.vpar[4] = 0; info->emu.vpar[5] = 8; if (info->dtmf_state) { @@ -997,9 +1065,8 @@ } isdn_all_eaz(info->isdn_driver, info->isdn_channel); info->emu.mdmreg[REG_RINGCNT] = 0; - usage = ((info->emu.mdmreg[REG_SI1I] != 1) || - (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? - ISDN_USAGE_MODEM : ISDN_USAGE_VOICE; + usage = isdn_calc_usage(info->emu.mdmreg[REG_SI1I], + info->emu.mdmreg[REG_L2PROT]); isdn_free_channel(info->isdn_driver, info->isdn_channel, usage); } @@ -1086,8 +1153,14 @@ si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -1140,11 +1213,11 @@ cmd.parm.cmsg.para[5] = l; strncpy(&cmd.parm.cmsg.para[6], id, l); cmd.command =CAPI_PUT_MESSAGE; -/* info->dialing = 1; - strcpy(dev->num[i], n); + info->dialing = 1; +// strcpy(dev->num[i], n); isdn_info_update(); -*/ isdn_command(&cmd); + isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); } } @@ -1174,8 +1247,14 @@ si = bit2si[j]; break; } + usg = isdn_calc_usage(si, l2); #ifdef CONFIG_ISDN_AUDIO - if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) { + if ((si == 1) && + (l2 != ISDN_PROTO_L2_MODEM) +#ifdef CONFIG_ISDN_TTY_FAX + && (l2 != ISDN_PROTO_L2_FAX) +#endif + ) { l2 = ISDN_PROTO_L2_TRANS; usg = ISDN_USAGE_VOICE; } @@ -2160,8 +2239,42 @@ m->vpar[1] = 0; /* Silence detection level (0 = none ) */ m->vpar[2] = 70; /* Silence interval (7 sec. ) */ m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */ - m->vpar[4] = 0; /* DTMF detection level (0 = softcode ) */ - m->vpar[5] = 8; /* DTMF interval (8 * 5 ms. ) */ + m->vpar[4] = 0; /* DTMF detection level (0 = softcode ) */ + m->vpar[5] = 8; /* DTMF interval (8 * 5 ms. ) */ +} +#endif + +#ifdef CONFIG_ISDN_TTY_FAX +static void +isdn_tty_modem_reset_faxpar(modem_info * info) +{ + T30_s *f = info->fax; + + f->code = 0; + f->phase = ISDN_FAX_PHASE_IDLE; + f->direction = 0; + f->resolution = 1; /* fine */ + f->rate = 5; /* 14400 bit/s */ + f->width = 0; + f->length = 0; + f->compression = 0; + f->ecm = 0; + f->binary = 0; + f->scantime = 0; + memset(&f->id[0], 32, FAXIDLEN - 1); + f->id[FAXIDLEN - 1] = 0; + f->badlin = 0; + f->badmul = 0; + f->bor = 0; + f->nbc = 0; + f->cq = 0; + f->cr = 0; + f->ctcrty = 0; + f->minsp = 0; + f->phcto = 30; + f->rel = 0; + memset(&f->pollid[0], 32, FAXIDLEN - 1); + f->pollid[FAXIDLEN - 1] = 0; } #endif @@ -2178,6 +2291,9 @@ #ifdef CONFIG_ISDN_AUDIO isdn_tty_modem_reset_vpar(m); #endif +#ifdef CONFIG_ISDN_TTY_FAX + isdn_tty_modem_reset_faxpar(info); +#endif m->mdmcmdl = 0; } @@ -2250,6 +2366,12 @@ } for (i = 0; i < ISDN_MAX_CHANNELS; i++) { info = &m->info[i]; +#ifdef CONFIG_ISDN_TTY_FAX + if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) { + printk(KERN_ERR "Could not allocate fax t30-buffer\n"); + return -3; + } +#endif #ifdef COMPAT_HAS_NEW_WAITQ init_MUTEX(&info->write_sem); #else @@ -2330,8 +2452,15 @@ break; } return ret; - } else - return isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di)); + } else { + int tmp; + tmp = isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di)); +#ifdef ISDN_DEBUG_MODEM_ICALL + printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n", + isdn_map_eaz2msn(emu->msn, di), tmp); +#endif + return tmp; + } } /* @@ -2383,7 +2512,7 @@ (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ idx = isdn_dc2minor(di, ch); #ifdef ISDN_DEBUG_MODEM_ICALL - printk(KERN_DEBUG "m_fi: match1\n"); + printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret); printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx, info->flags, info->isdn_driver, info->isdn_channel, dev->usage[idx]); @@ -2405,8 +2534,7 @@ info->drv_index = idx; dev->m_idx[idx] = info->line; dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; - dev->usage[idx] |= ((si1 != 1) || (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ? - ISDN_USAGE_MODEM : ISDN_USAGE_VOICE; + dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); strcpy(dev->num[idx], nr); strcpy(info->emu.cpn, eaz); info->emu.mdmreg[REG_SI1I] = si2bit[si1]; @@ -2584,6 +2712,13 @@ } } return 1; +#ifdef CONFIG_ISDN_TTY_FAX + case ISDN_STAT_FAXIND: + if (TTY_IS_ACTIVE(info)) { + isdn_tty_fax_command(info); + } + break; +#endif #ifdef CONFIG_ISDN_AUDIO case ISDN_STAT_AUDIO: if (TTY_IS_ACTIVE(info)) { @@ -2621,6 +2756,8 @@ char *p; char c; ulong flags; + struct sk_buff *skb = 0; + char *sp = 0; if (!msg) { printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n"); @@ -2629,6 +2766,34 @@ save_flags(flags); cli(); tty = info->tty; + if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { + restore_flags(flags); + return; + } + + /* use queue instead of direct flip, if online and */ + /* data is in queue or flip buffer is full */ + if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) || + (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) { + skb = alloc_skb(strlen(msg) +#ifdef CONFIG_ISDN_AUDIO + + sizeof(isdn_audio_skb) +#endif + , GFP_ATOMIC); + if (!skb) { + restore_flags(flags); + return; + } +#ifdef CONFIG_ISDN_AUDIO + skb_reserve(skb, sizeof(isdn_audio_skb)); +#endif + sp = skb_put(skb, strlen(msg)); +#ifdef CONFIG_ISDN_AUDIO + ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; + ISDN_AUDIO_SKB_LOCK(skb) = 0; +#endif + } + for (p = msg; *p; p++) { switch (*p) { case '\r': @@ -2643,16 +2808,26 @@ default: c = *p; } - if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { - restore_flags(flags); - return; + if (skb) { + *sp++ = c; + } else { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + tty_insert_flip_char(tty, c, 0); } - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - break; - tty_insert_flip_char(tty, c, 0); } - restore_flags(flags); - queue_task(&tty->flip.tqueue, &tq_timer); + if (skb) { + __skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb); + dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len; + restore_flags(flags); + /* Schedule dequeuing */ + if ((dev->modempoll) && (info->rcvsched)) + isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); + + } else { + restore_flags(flags); + queue_task(&tty->flip.tqueue, &tq_timer); + } } /* @@ -3328,7 +3503,7 @@ /* If more than one bit set in reg18, autoselect Layer2 */ if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) { if (m->mdmreg[REG_SI1I] == 1) { - if (l2 != ISDN_PROTO_L2_MODEM) + if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX)) l2 = ISDN_PROTO_L2_TRANS; } else l2 = ISDN_PROTO_L2_X75I; @@ -3342,6 +3517,12 @@ cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); +#ifdef CONFIG_ISDN_TTY_FAX + if (l2 == ISDN_PROTO_L2_FAX) { + cmd.parm.fax = info->fax; + info->fax->direction = ISDN_TTY_FAX_CONN_IN; + } +#endif isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; @@ -3362,7 +3543,6 @@ isdn_tty_cmd_PLUSF(char **p, modem_info * info) { atemu *m = &info->emu; - int par; char rs[20]; if (!strncmp(p[0], "CLASS", 5)) { @@ -3372,6 +3552,10 @@ p[0]++; sprintf(rs, "\r\n%d", (m->mdmreg[REG_SI1] & 1) ? 8 : 0); +#ifdef CONFIG_ISDN_TTY_FAX + if (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) + sprintf(rs, "\r\n2"); +#endif isdn_tty_at_cout(rs, info); break; case '=': @@ -3379,23 +3563,37 @@ switch (*p[0]) { case '0': p[0]++; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; m->mdmreg[REG_SI1] = 4; info->xmit_size = m->mdmreg[REG_PSIZE] * 16; break; +#ifdef CONFIG_ISDN_TTY_FAX case '2': - printk(KERN_DEBUG "isdn_tty: FCLASS=2\n"); p[0]++; + m->mdmreg[REG_SI1] = 1; + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FAX; + info->xmit_size = + m->mdmreg[REG_PSIZE] * 16; break; +#endif case '8': p[0]++; + /* L2 will change on dialout with si=1 */ + m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; + m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; m->mdmreg[REG_SI1] = 5; info->xmit_size = VBUF; break; case '?': p[0]++; - isdn_tty_at_cout("\r\n0,2,8", - info); +#ifdef CONFIG_ISDN_TTY_FAX + isdn_tty_at_cout("\r\n0,2,8", info); +#else + isdn_tty_at_cout("\r\n0,8", info); +#endif break; default: PARSE_ERROR1; @@ -3406,115 +3604,11 @@ } return 0; } - if (!strncmp(p[0], "AA", 2)) { - p[0] += 2; - switch (*p[0]) { - case '?': - p[0]++; - sprintf(rs, "\r\n%d", - m->mdmreg[REG_RINGATA]); - isdn_tty_at_cout(rs, info); - break; - case '=': - p[0]++; - par = isdn_getnum(p); - if ((par < 0) || (par > 255)) - PARSE_ERROR1; - m->mdmreg[REG_RINGATA] = par; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "TBC=", 4)) { /* UNKLAR !! */ - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); - switch (*p[0]) { - case '0': - p[0]++; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "BOR=", 4)) { /* UNKLAR !! */ - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: Fax FBOR=%c\n", *p[0]); - switch (*p[0]) { - case '0': - p[0]++; - break; - default: - PARSE_ERROR1; - } - return 0; - } - if (!strncmp(p[0], "DCC=", 4)) { /* SETUP irgendwie !! */ - int i, val[]={0,0,0,0,0,0,0,0}; - - p[0] += 4; - if (*p[0] == '?') { - isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0,1),(0),(0),(0-7)",info); - p[0]++; - } else { - for (i=0; (*p[0]>='0') && (*p[0]<='9'); i++) { - val[i] = *p[0] - 48; - p[0]++; - if (*p[0] == ',') - p[0]++; - } - printk(KERN_DEBUG "isdn_tty: Fax Setup values=%d,%d,%d,%d,%d,%d,%d,%d\n", - val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); - } - return 0; - } - if (!strncmp(p[0], "LID=", 4)) { /* set sender ID !! */ - char senderID[80]; - int i; - - p[0] += 4; - if (*p[0] =='"') - p[0]++; - for(i=0; (*p[0]) && (*p[0] != '"'); i++) - senderID[i] = *p[0]++; - senderID[i] = 0; - if (*p[0] =='"') - p[0]++; - printk(KERN_DEBUG "isdn_tty: Fax sender=>%s<\n", senderID); - return 0; - } - if (!strncmp(p[0], "MFR?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FMFR?\n"); - isdn_tty_at_cout("\r\nISDNfax", info); - return 0; - } - if (!strncmp(p[0], "MDL?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FMDL?\n"); - isdn_tty_at_cout("\r\nAVM-B1", info); - return 0; - } - if (!strncmp(p[0], "AP=?", 4)) { - p[0] += 4; - printk(KERN_DEBUG "isdn_tty: FAP=?\n"); - return 0; - } - if (!strncmp(p[0], "PHCTO=", 6)) { - /* beim trace mit dem zyxel folgt der wert 30 ;*/ - p[0] += 6; - printk(KERN_DEBUG "isdn_tty: FPHCTO=%s\n", p[0]); - return 0; - } - if (!strncmp(p[0], "CR=", 3)) { - p[0] += 3; - printk(KERN_DEBUG "isdn_tty: FCR=%s\n", p[0]); - return 0; - } - printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); +#ifdef CONFIG_ISDN_TTY_FAX + return (isdn_tty_cmd_PLUSF_FAX(p, info)); +#else PARSE_ERROR1; +#endif } /* @@ -3959,8 +4053,10 @@ isdn_tty_suspend(ds, info, m); break; case 'R': /* RESUME */ + p++; isdn_tty_get_msnstr(ds, &p); isdn_tty_resume(ds, info, m); + break; case 'M': /* MESSAGE */ p++; isdn_tty_send_msg(info, m, p); diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h --- v2.2.11/linux/drivers/isdn/isdn_tty.h Mon Aug 9 16:05:56 1999 +++ linux/drivers/isdn/isdn_tty.h Wed Aug 25 17:29:48 1999 @@ -1,4 +1,4 @@ -/* $Id: isdn_tty.h,v 1.14 1999/07/11 17:14:15 armin Exp $ +/* $Id: isdn_tty.h,v 1.15 1999/07/31 12:59:48 armin Exp $ * header for Linux ISDN subsystem, tty related functions (linklevel). * @@ -20,6 +20,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.h,v $ + * Revision 1.15 1999/07/31 12:59:48 armin + * Added tty fax capabilities. + * * Revision 1.14 1999/07/11 17:14:15 armin * Added new layer 2 and 3 protocols for Fax and DSP functions. * Moved "Add CPN to RING message" to new register S23, @@ -157,3 +160,9 @@ extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *); extern int isdn_tty_capi_facility(capi_msg *cm); extern void isdn_tty_at_cout(char *, modem_info *); +extern void isdn_tty_modem_hup(modem_info *, int); +#ifdef CONFIG_ISDN_TTY_FAX +extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *); +extern int isdn_tty_fax_command(modem_info *); +extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *); +#endif diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/isdn_ttyfax.c linux/drivers/isdn/isdn_ttyfax.c --- v2.2.11/linux/drivers/isdn/isdn_ttyfax.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/isdn_ttyfax.c Wed Aug 25 17:29:48 1999 @@ -0,0 +1,1201 @@ +/* $Id: isdn_ttyfax.c,v 1.2 1999/08/05 10:36:10 armin Exp $ + * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel). + * + * Copyright 1999 by Armin Schindler (mac@melware.de) + * Copyright 1999 by Ralf Spachmann (mel@melware.de) + * Copyright 1999 by Cytronics & Melware + * + * 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. + * + * $Log: isdn_ttyfax.c,v $ + * Revision 1.2 1999/08/05 10:36:10 armin + * Bugfix: kernel oops on getting revision. + * + * Revision 1.1 1999/07/31 12:59:50 armin + * Added tty fax capabilities. + * + * + */ + +#undef ISDN_TTY_FAX_STAT_DEBUG +#undef ISDN_TTY_FAX_CMD_DEBUG + +#define __NO_VERSION__ +#include +#include +#include +#include "isdn_common.h" +#include "isdn_tty.h" +#include "isdn_ttyfax.h" + + +static char *isdn_tty_fax_revision = "$Revision: 1.2 $"; + +#define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; } + +static char * +isdn_getrev(const char *revision) +{ + char *rev; + char *p; + + if ((p = strchr(revision, ':'))) { + rev = p + 2; + p = strchr(rev, '$'); + *--p = 0; + } else + rev = "???"; + return rev; +} + + +/* + * Fax Class 2 Modem results + * + */ +static void isdn_tty_fax_modem_result(int code, modem_info * info) +{ + atemu *m = &info->emu; + T30_s *f = info->fax; + char rs[50]; + char rss[50]; + char *rp; + int i; + static char *msg[] = + {"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:", + "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:", + "+FCFR", "+FPTS:", "+FET:" }; + + + isdn_tty_at_cout("\r\n", info); + isdn_tty_at_cout(msg[code], info); + +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n", + msg[code], info->line); +#endif + switch (code) { + case 0: /* OK */ + break; + case 1: /* ERROR */ + break; + case 2: /* +FCON */ + /* Append CPN, if enabled */ + if ((m->mdmreg[REG_CPN] & BIT_CPN) && + (!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) { + sprintf(rs, "/%s", m->cpn); + isdn_tty_at_cout(rs, info); + } + info->online = 1; + f->fet = 0; + if (f->phase == ISDN_FAX_PHASE_A) + f->phase = ISDN_FAX_PHASE_B; + break; + case 3: /* +FCSI */ + case 8: /* +FTSI */ + sprintf(rs, "\"%s\"", f->r_id); + isdn_tty_at_cout(rs, info); + break; + case 4: /* +FDIS */ + rs[0] = 0; + rp = &f->r_resolution; + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n", + rs, info->line); +#endif + break; + case 5: /* +FHNG */ + sprintf(rs, "%d", f->code); + isdn_tty_at_cout(rs, info); + info->faxonline = 0; + break; + case 6: /* +FDCS */ + rs[0] = 0; + rp = &f->r_resolution; + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n", + rs, info->line); +#endif + break; + case 7: /* CONNECT */ + info->faxonline |= 2; + break; + case 9: /* FCFR */ + break; + case 10: /* FPTS */ + isdn_tty_at_cout("1", info); + break; + case 11: /* FET */ + sprintf(rs, "%d", f->fet); + isdn_tty_at_cout(rs, info); + break; + } + + isdn_tty_at_cout("\r\n", info); + + switch (code) { + case 7: /* CONNECT */ + info->online = 2; + if (info->faxonline & 1) { + sprintf(rs, "%c", XON); + isdn_tty_at_cout(rs, info); + } + break; + } +} + +int +isdn_tty_fax_command(modem_info * info) +{ + T30_s *f = info->fax; + char rs[10]; + +#ifdef ISDN_TTY_FAX_CMD_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n", + f->r_code, info->line); +#endif + switch(f->r_code) { + case ISDN_TTY_FAX_FCON: + info->faxonline = 1; + isdn_tty_fax_modem_result(2, info); /* +FCON */ + return(0); + case ISDN_TTY_FAX_FCON_I: + info->faxonline = 16; + isdn_tty_fax_modem_result(2, info); /* +FCON */ + return(0); + case ISDN_TTY_FAX_RID: + if (info->faxonline & 1) + isdn_tty_fax_modem_result(3, info); /* +FCSI */ + if (info->faxonline & 16) + isdn_tty_fax_modem_result(8, info); /* +FTSI */ + return(0); + case ISDN_TTY_FAX_DIS: + isdn_tty_fax_modem_result(4, info); /* +FDIS */ + return(0); + case ISDN_TTY_FAX_HNG: + if (f->phase == ISDN_FAX_PHASE_C) { + if (f->direction == ISDN_TTY_FAX_CONN_IN) { + sprintf(rs, "%c%c", DLE, ETX); + isdn_tty_at_cout(rs, info); + } else { + sprintf(rs, "%c", 0x18); + isdn_tty_at_cout(rs, info); + } + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + } + f->phase = ISDN_FAX_PHASE_E; + isdn_tty_fax_modem_result(5, info); /* +FHNG */ + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_DCS: + isdn_tty_fax_modem_result(6, info); /* +FDCS */ + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + f->phase = ISDN_FAX_PHASE_C; + return(0); + case ISDN_TTY_FAX_TRAIN_OK: + isdn_tty_fax_modem_result(6, info); /* +FDCS */ + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_SENT: + isdn_tty_fax_modem_result(0, info); /* OK */ + return(0); + case ISDN_TTY_FAX_CFR: + isdn_tty_fax_modem_result(9, info); /* +FCFR */ + return(0); + case ISDN_TTY_FAX_ET: + sprintf(rs, "%c%c", DLE, ETX); + isdn_tty_at_cout(rs, info); + isdn_tty_fax_modem_result(10, info); /* +FPTS */ + isdn_tty_fax_modem_result(11, info); /* +FET */ + isdn_tty_fax_modem_result(0, info); /* OK */ + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + f->phase = ISDN_FAX_PHASE_D; + return(0); + case ISDN_TTY_FAX_PTS: + isdn_tty_fax_modem_result(10, info); /* +FPTS */ + if (f->direction == ISDN_TTY_FAX_CONN_OUT) { + if (f->fet == 1) + f->phase = ISDN_FAX_PHASE_B; + if (f->fet == 0) + isdn_tty_fax_modem_result(0, info); /* OK */ + } + return(0); + case ISDN_TTY_FAX_EOP: + info->faxonline &= ~2; /* leave data mode */ + info->online = 1; + f->phase = ISDN_FAX_PHASE_D; + return(0); + + } + return(-1); +} + + +void +isdn_tty_fax_bitorder(modem_info *info, struct sk_buff *skb) +{ + __u8 LeftMask; + __u8 RightMask; + __u8 fBit; + __u8 Data; + int i; + + if (!info->fax->bor) { + for(i = 0; i < skb->len; i++) { + Data = skb->data[i]; + for ( + LeftMask = 0x80, RightMask = 0x01; + LeftMask > RightMask; + LeftMask >>= 1, RightMask <<= 1 + ) { + fBit = (Data & LeftMask); + if (Data & RightMask) + Data |= LeftMask; + else + Data &= ~LeftMask; + if (fBit) + Data |= RightMask; + else + Data &= ~RightMask; + + } + skb->data[i] = Data; + } + } +} + +/* + * Parse AT+F.. FAX class 2 commands + */ + +int isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) +{ + atemu *m = &info->emu; + T30_s *f = info->fax; + isdn_ctrl cmd; + int par; + char rs[50]; + char rss[50]; + int maxdccval[]={1,5,2,2,3,2,0,7}; + + /* FAA still unchanged */ + if (!strncmp(p[0], "AA", 2)) { /* TODO */ + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", 0); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */ + if (!strncmp(p[0], "BADLIN", 6)) { + p[0] += 6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->badlin); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->badlin = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */ + if (!strncmp(p[0], "BADMUL", 6)){ + p[0] +=6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->badmul); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->badmul = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BOR=n - Phase C bit order, 0=direct, 1=reverse */ + if (!strncmp(p[0], "BOR", 3)){ + p[0] +=3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->bor); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->bor = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* NBC=n - No Best Capabilities */ + if (!strncmp(p[0], "NBC", 3)){ + p[0] +=3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->nbc); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->nbc = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* BUF? - Readonly buffersize readout */ + if (!strncmp(p[0], "BUF?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE])); +#endif + p[0]++; + sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE])); + isdn_tty_at_cout(rs, info); + return 0; + } + + /* CIG=string - local fax station id string for polling rx */ + if (!strncmp(p[0], "CIG", 3)) { + int i, r; + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n\"%s\"", f->pollid); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n\"STRING\""); + isdn_tty_at_cout(rs, info); + } + else + { + if (*p[0] =='"') + p[0]++; + for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++) + { + f->pollid[i] = *p[0]++; + } + if (*p[0] =='"') + p[0]++; + for(r=i; r < FAXIDLEN; r++) + { + f->pollid[r] = 32; + } + f->pollid[FAXIDLEN-1] = 0; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */ + if (!strncmp(p[0], "CQ", 2)) { + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->cq); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->cq = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */ + if (!strncmp(p[0], "CR", 2)) { + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->cr); /* read actual value from struct and print */ + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); /* display online help */ + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->cr = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* CTCRTY=value - ECM retry count */ + if (!strncmp(p[0], "CTCRTY", 6)){ + p[0] +=6; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->ctcrty); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->ctcrty = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */ + if (!strncmp(p[0], "DCC", 3)) { + char *rp = &f->resolution; + int i; + + p[0] += 3; + switch(*p[0]) { + case '?': + p[0]++; + strcpy(rs, "\r\n"); + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') { + isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info); + p[0]++; + } else { + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[i]) { + PARSE_ERROR1; + } + rp[i] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n", + rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */ + if (!strncmp(p[0], "DIS", 3)) { + char *rp = &f->resolution; + int i; + + p[0] += 3; + switch(*p[0]) { + case '?': + p[0]++; + strcpy(rs, "\r\n"); + for(i = 0; i < 8; i++) { + sprintf(rss, "%c%s", rp[i] + 48, + (i < 7) ? "," : ""); + strcat(rs, rss); + } + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') { + isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)",info); + p[0]++; + } else { + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<8); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[i]) { + PARSE_ERROR1; + } + rp[i] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n", + rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* DR - Receive Phase C data command, initiates document reception */ + if (!strncmp(p[0], "DR", 2)) { + p[0] += 2; + if ((info->faxonline & 16) && /* incoming connection */ + ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) { +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDR\n"); +#endif + f->code = ISDN_TTY_FAX_DR; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); + if (f->phase == ISDN_FAX_PHASE_B) { + f->phase = ISDN_FAX_PHASE_C; + } else if (f->phase == ISDN_FAX_PHASE_D) { + switch(f->fet) { + case 0: /* next page will be received */ + f->phase = ISDN_FAX_PHASE_C; + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + break; + case 1: /* next doc will be received */ + f->phase = ISDN_FAX_PHASE_B; + break; + case 2: /* fax session is terminating */ + f->phase = ISDN_FAX_PHASE_E; + break; + default: + PARSE_ERROR1; + } + } + } else { + PARSE_ERROR1; + } + return 1; + } + + /* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */ + if (!strncmp(p[0], "DT", 2)) { + int i, val[]={4,0,2,3}; + char *rp = &f->resolution; + + p[0] += 2; + if (!info->faxonline & 1) /* not outgoing connection */ + PARSE_ERROR1; + + for (i=0; (((*p[0]>='0')&&(*p[0]<='9'))||(*p[0]==','))&&(i<4); i++) { + if (*p[0] != ',') { + if ((*p[0] - 48) > maxdccval[val[i]]) { + PARSE_ERROR1; + } + rp[val[i]] = *p[0] - 48; + p[0]++; + if (*p[0] == ',') + p[0]++; + } else p[0]++; + } +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n", + rp[4], rp[0], rp[2], rp[3]); +#endif + if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) { + f->code = ISDN_TTY_FAX_DT; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); + if (f->phase == ISDN_FAX_PHASE_D) { + f->phase = ISDN_FAX_PHASE_C; + isdn_tty_fax_modem_result(7, info); /* CONNECT */ + } + } else { + PARSE_ERROR1; + } + return 1; + } + + /* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */ + if (!strncmp(p[0], "ECM", 3)) { + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->ecm); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par != 0) && (par != 2)) + PARSE_ERROR1; + f->ecm = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* ET=n - End of page or document */ + if (!strncmp(p[0], "ET=", 3)) { + p[0] += 3; + if (*p[0] == '?') { + p[0]++; + sprintf(rs, "\r\n0-2"); + isdn_tty_at_cout(rs, info); + } else { + if ((f->phase != ISDN_FAX_PHASE_D) || (!info->faxonline & 1)) + PARSE_ERROR1; + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->fet = par; + f->code = ISDN_TTY_FAX_ET; + cmd.driver = info->isdn_driver; + cmd.arg = info->isdn_channel; + cmd.command = ISDN_CMD_FAXCMD; + isdn_command(&cmd); +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par); +#endif + return 1; + } + return 0; + } + + /* K - terminate */ + if (!strncmp(p[0], "K", 1)) { + p[0] += 1; + if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E)) + PARSE_ERROR1; + isdn_tty_modem_hup(info, 1); + return 1; + } + + /* LID=string - local fax ID */ + if (!strncmp(p[0], "LID", 3)) { + int i, r; + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n\"%s\"", f->id); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n\"STRING\""); + isdn_tty_at_cout(rs, info); + } + else + { + if (*p[0] =='"') + p[0]++; + for(i=0; (*p[0]) && i < (FAXIDLEN-1) && (*p[0] != '"'); i++) + { + f->id[i] = *p[0]++; + } + if (*p[0] =='"') + p[0]++; + for(r=i; r < FAXIDLEN; r++) + { + f->id[r] = 32; + } + f->id[FAXIDLEN-1] = 0; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + +#if 0 + /* LO=n - Flow control opts */ + if (!strncmp(p[0], "LO", 2)) { /* TODO */ + p[0] += 2; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->lo); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1,2"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 2)) + PARSE_ERROR1; + f->lo = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FLO=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif +#if 0 + /* LPL=n - Doc for polling cmd */ + if (!strncmp(p[0], "LPL", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->lpl); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->lpl = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FLPL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* MDL? - DCE Model */ + if (!strncmp(p[0], "MDL?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FMDL?\n"); +#endif + isdn_tty_at_cout("\r\nisdn4linux", info); + return 0; + } + + /* MFR? - DCE Manufacturer */ + if (!strncmp(p[0], "MFR?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FMFR?\n"); +#endif + isdn_tty_at_cout("\r\nisdn4linux", info); + return 0; + } + + /* MINSP=n - Minimum Speed for Phase C */ + if (!strncmp(p[0], "MINSP", 5)) { + p[0] += 5; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->minsp); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-5"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 5)) + PARSE_ERROR1; + f->minsp = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* PHCTO=value - DTE phase C timeout */ + if (!strncmp(p[0], "PHCTO", 5)){ + p[0] +=5; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->phcto); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-255"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 255)) + PARSE_ERROR1; + f->phcto = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + +#if 0 + /* PTS=n - Page transfer status */ + if (!strncmp(p[0], "PTS", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->pts); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0-5"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 5)) + PARSE_ERROR1; + f->pts = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FPTS=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* REL=n - Phase C received EOL alignment */ + if (!strncmp(p[0], "REL", 3)) { + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d",f->rel); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->rel = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } + + /* REV? - DCE Revision */ + if (!strncmp(p[0], "REV?", 4)) { + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: FREV?\n"); +#endif + strcpy(rss, isdn_tty_fax_revision); + sprintf(rs, "\r\nRev: %s", isdn_getrev(rss)); + isdn_tty_at_cout(rs, info); + return 0; + } + +#if 0 + /* SPL=n - Enable polling */ + if (!strncmp(p[0], "SPL", 3)) { /* TODO */ + p[0] += 3; + switch (*p[0]) { + case '?': + p[0]++; + sprintf(rs, "\r\n%d", f->spl); + isdn_tty_at_cout(rs, info); + break; + case '=': + p[0]++; + if (*p[0] == '?') + { + p[0]++; + sprintf(rs, "\r\n0,1"); + isdn_tty_at_cout(rs, info); + } + else + { + par = isdn_getnum(p); + if ((par < 0) || (par > 1)) + PARSE_ERROR1; + f->spl = par; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FSPL=%d\n", par); +#endif + } + break; + default: + PARSE_ERROR1; + } + return 0; + } +#endif + + /* Phase C Transmit Data Block Size */ + if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */ + p[0] += 4; +#ifdef ISDN_TTY_FAX_STAT_DEBUG + printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); +#endif + switch (*p[0]) { + case '0': + p[0]++; + break; + default: + PARSE_ERROR1; + } + return 0; + } + + printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); + PARSE_ERROR1; +} + diff -u --recursive --new-file v2.2.11/linux/drivers/isdn/isdn_ttyfax.h linux/drivers/isdn/isdn_ttyfax.h --- v2.2.11/linux/drivers/isdn/isdn_ttyfax.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/isdn_ttyfax.h Wed Aug 25 17:29:48 1999 @@ -0,0 +1,33 @@ +/* $Id: isdn_ttyfax.h,v 1.1 1999/07/31 12:59:51 armin Exp $ + * header for Linux ISDN subsystem, tty_fax related functions (linklevel). + * + * Copyright 1999 by Armin Schindler (mac@melware.de) + * Copyright 1999 by Ralf Spachmann (mel@melware.de) + * Copyright 1999 by Cytronics & Melware + * + * 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. + * + * $Log: isdn_ttyfax.h,v $ + * Revision 1.1 1999/07/31 12:59:51 armin + * Added tty fax capabilities. + * + * + */ + + +#define XON 0x11 +#define XOFF 0x13 +#define DC2 0x12 + diff -u --recursive --new-file v2.2.11/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c --- v2.2.11/linux/drivers/macintosh/adb.c Thu Apr 29 12:53:48 1999 +++ linux/drivers/macintosh/adb.c Wed Aug 25 17:29:48 1999 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -39,13 +40,13 @@ struct adb_controller *adb_controller = NULL; struct notifier_block *adb_client_list = NULL; enum adb_hw adb_hardware = ADB_NONE; +static int adb_got_sleep = 0; #ifdef CONFIG_PMAC_PBOOK -static int adb_notify_sleep(struct notifier_block *, unsigned long, void *); -static struct notifier_block adb_sleep_notifier = { +static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when); +static struct pmu_sleep_notifier adb_sleep_notifier = { adb_notify_sleep, - NULL, - 0 + SLEEP_LEVEL_ADB, }; #endif @@ -173,8 +174,7 @@ { adb_hardware = adb_controller->kind; #ifdef CONFIG_PMAC_PBOOK - notifier_chain_register(&sleep_notifier_list, - &adb_sleep_notifier); + pmu_register_sleep_notifier(&adb_sleep_notifier); #endif /* CONFIG_PMAC_PBOOK */ adb_reset_bus(); @@ -187,20 +187,31 @@ * notify clients before sleep and reset bus afterwards */ int -adb_notify_sleep(struct notifier_block *this, unsigned long code, void *x) +adb_notify_sleep(struct pmu_sleep_notifier *self, int when) { int ret; - switch (code) { - case PBOOK_SLEEP: - ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); - if (ret & NOTIFY_STOP_MASK) - return -EBUSY; - case PBOOK_WAKE: + switch (when) { + case PBOOK_SLEEP_REQUEST: + adb_got_sleep = 1; + ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); + if (ret & NOTIFY_STOP_MASK) + return PBOOK_SLEEP_REFUSE; + break; + case PBOOK_SLEEP_REJECT: + if (adb_got_sleep) { + adb_got_sleep = 0; adb_reset_bus(); - break; + } + break; + + case PBOOK_SLEEP_NOW: + break; + case PBOOK_WAKE: + adb_reset_bus(); + break; } - return NOTIFY_DONE; + return PBOOK_SLEEP_OK; } #endif /* CONFIG_PMAC_PBOOK */ diff -u --recursive --new-file v2.2.11/linux/drivers/macintosh/mac_keyb.c linux/drivers/macintosh/mac_keyb.c --- v2.2.11/linux/drivers/macintosh/mac_keyb.c Thu Apr 29 12:53:48 1999 +++ linux/drivers/macintosh/mac_keyb.c Wed Aug 25 17:29:48 1999 @@ -16,13 +16,14 @@ * * - Standard 1 button mouse * - All standard Apple Extended protocol (handler ID 4) - * mice & trackballs + * - mouseman and trackman mice & trackballs * - PowerBook Trackpad (default setup: enable tapping) * - MicroSpeed mouse & trackball (needs testing) * - CH Products Trackball Pro (needs testing) * - Contour Design (Contour Mouse) * - Hunter digital (NoHandsMouse) * - Kensignton TurboMouse 5 (needs testing) + * - Mouse Systems A3 mice and trackballs * * To do: * @@ -39,6 +40,7 @@ #include #include #include +#include #include #include @@ -66,10 +68,10 @@ 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, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* esc...option */ - 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* fn, num lock */ + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, /* fn, num lock */ 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, 1, 0, 0, 0, 0, /* scroll lock */ - 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, 1, 1, 1, 0, 0, /* R modifiers */ }; /* Simple translation table for the SysRq keys */ @@ -239,6 +241,7 @@ static void init_trackball(int id); static void init_turbomouse(int id); static void init_microspeed(int id); +static void init_ms_a3(int id); #ifdef CONFIG_ADBMOUSE /* XXX: Hook for mouse driver */ @@ -268,6 +271,7 @@ #define ADBMOUSE_TURBOMOUSE5 5 /* Turbomouse 5 (previously req. mousehack) */ #define ADBMOUSE_MICROSPEED 6 /* Microspeed mouse (&trackball ?), MacPoint */ #define ADBMOUSE_TRACKBALLPRO 7 /* Trackball Pro (special buttons) */ +#define ADBMOUSE_MS_A3 8 /* Mouse systems A3 trackball (handler 3) */ static int adb_mouse_kinds[16]; @@ -512,6 +516,11 @@ data[2] = (data[2] & 0x7f) | ((data[3] & 0x01) << 7); data[3] = (data[3] & 0x77) | ((data[3] & 0x02) << 6); break; + case ADBMOUSE_MS_A3: + data[1] = (data[1] & 0x7f) | ((data[3] & 0x01) << 7); + data[2] = (data[2] & 0x7f) | ((data[3] & 0x02) << 6); + data[3] = ((data[3] & 0x04) << 5); + break; } if (adb_mouse_interrupt_hook) @@ -741,9 +750,12 @@ /* Enable full feature set of the keyboard ->get it to send separate codes for left and right shift, control, option keys */ +#if 0 /* handler 5 doesn't send separate codes for R modifiers */ if (adb_try_handler_change(id, 5)) printk("ADB keyboard at %d, handler set to 5\n", id); - else if (adb_try_handler_change(id, 3)) + else +#endif + if (adb_try_handler_change(id, 3)) printk("ADB keyboard at %d, handler set to 3\n", id); else printk("ADB keyboard at %d, handler 1\n", id); @@ -757,10 +769,6 @@ printk("ADB mouse at %d, handler set to 4", id); adb_mouse_kinds[id] = ADBMOUSE_EXTENDED; } - else if (adb_try_handler_change(id, 2)) { - printk("ADB mouse at %d, handler set to 2", id); - adb_mouse_kinds[id] = ADBMOUSE_STANDARD_200; - } else if (adb_try_handler_change(id, 0x2F)) { printk("ADB mouse at %d, handler set to 0x2F", id); adb_mouse_kinds[id] = ADBMOUSE_MICROSPEED; @@ -777,6 +785,14 @@ printk("ADB mouse at %d, handler set to 0x5F", id); adb_mouse_kinds[id] = ADBMOUSE_MICROSPEED; } + else if (adb_try_handler_change(id, 3)) { + printk("ADB mouse at %d, handler set to 3", id); + adb_mouse_kinds[id] = ADBMOUSE_MS_A3; + } + else if (adb_try_handler_change(id, 2)) { + printk("ADB mouse at %d, handler set to 2", id); + adb_mouse_kinds[id] = ADBMOUSE_STANDARD_200; + } else { printk("ADB mouse at %d, handler 1", id); adb_mouse_kinds[id] = ADBMOUSE_STANDARD_100; @@ -785,6 +801,8 @@ if ((adb_mouse_kinds[id] == ADBMOUSE_TRACKBALLPRO) || (adb_mouse_kinds[id] == ADBMOUSE_MICROSPEED)) { init_microspeed(id); + } else if (adb_mouse_kinds[id] == ADBMOUSE_MS_A3) { + init_ms_a3(id); } else if (adb_mouse_kinds[id] == ADBMOUSE_EXTENDED) { /* * Register 1 is usually used for device @@ -796,7 +814,8 @@ ADB_READREG(id, 1)); if ((req.reply_len) && - (req.reply[1] == 0x9a) && (req.reply[2] == 0x21)) + (req.reply[1] == 0x9a) && ((req.reply[2] == 0x21) + || (req.reply[2] == 0x20))) init_trackball(id); else if ((req.reply_len >= 4) && (req.reply[1] == 0x74) && (req.reply[2] == 0x70) && @@ -868,7 +887,7 @@ { struct adb_request req; - printk(" (trackball)"); + printk(" (trackman/mouseman)"); adb_mouse_kinds[id] = ADBMOUSE_TRACKBALL; @@ -908,13 +927,10 @@ adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); - adb_request(&req, NULL, ADBREQ_SYNC, 3, - ADB_WRITEREG(id,3), 0x20 | id, 4); - - adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); + adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(3)); adb_request(&req, NULL, ADBREQ_SYNC, 9, - ADB_WRITEREG(id,2), + ADB_WRITEREG(3,2), 0xe7, 0x8c, 0, @@ -924,10 +940,10 @@ 0xff, 0x94); - adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); + adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(3)); adb_request(&req, NULL, ADBREQ_SYNC, 9, - ADB_WRITEREG(id,2), + ADB_WRITEREG(3,2), 0xa5, 0x14, 0, @@ -977,4 +993,18 @@ adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); } + +static void +init_ms_a3(int id) +{ + struct adb_request req; + + printk(" (Mouse Systems A3 Mouse, or compatible)"); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(id, 0x2), + 0x00, + 0x07); + + adb_request(&req, NULL, ADBREQ_SYNC, 1, ADB_FLUSH(id)); + } diff -u --recursive --new-file v2.2.11/linux/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c --- v2.2.11/linux/drivers/macintosh/via-pmu.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/macintosh/via-pmu.c Wed Aug 25 17:29:48 1999 @@ -1,3 +1,4 @@ + /* * Device driver for the via-pmu on Apple Powermacs. * @@ -33,6 +34,7 @@ #include #include #include +#include /* Misc minor number allocated for /dev/pmu */ #define PMU_MINOR 154 @@ -71,6 +73,7 @@ #define IER_SET 0x80 /* set bits in IER */ #define IER_CLR 0 /* clear bits in IER */ #define SR_INT 0x04 /* Shift register full/empty */ +#define CB2_INT 0x08 #define CB1_INT 0x10 /* transition on CB1 input */ static enum pmu_state { @@ -125,6 +128,16 @@ pmu_poll }; +extern void low_sleep_handler(void); +extern void sleep_save_intrs(int); +extern void sleep_restore_intrs(void); + +extern int grackle_pcibios_read_config_word(unsigned char bus, + unsigned char dev_fn, unsigned char offset, unsigned short *val); + +extern int grackle_pcibios_write_config_word(unsigned char bus, + unsigned char dev_fn, unsigned char offset, unsigned short val); + /* * This table indicates for each PMU opcode: * - the number of data bytes to be sent with the command, or -1 @@ -168,13 +181,21 @@ /*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}, }; +static char *pbook_type[] = { + "Unknown PowerBook", + "PowerBook 2400/3400/3500(G3)", + "PowerBook G3 Series", + "1999 PowerBook G3", +}; -void __openfirmware +int __openfirmware find_via_pmu() { + if (via != 0) + return 1; vias = find_devices("via-pmu"); if (vias == 0) - return; + return 0; if (vias->next != 0) printk(KERN_WARNING "Warning: only using 1st via-pmu\n"); @@ -196,12 +217,14 @@ printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n", vias->n_addrs, vias->n_intrs); if (vias->n_addrs < 1 || vias->n_intrs < 1) - return; + return 0; } if (vias->parent->name && ((strcmp(vias->parent->name, "ohare") == 0) || device_is_compatible(vias->parent, "ohare"))) pmu_kind = PMU_OHARE_BASED; + else if (device_is_compatible(vias->parent, "paddington")) + pmu_kind = PMU_PADDINGTON_BASED; else if (device_is_compatible(vias->parent, "heathrow")) pmu_kind = PMU_HEATHROW_BASED; else @@ -220,9 +243,9 @@ if (via) printk(KERN_INFO "PMU driver initialized for %s\n", - (pmu_kind == PMU_OHARE_BASED) ? "PowerBook 2400/3400/3500(G3)" : - ((pmu_kind == PMU_HEATHROW_BASED) ? "PowerBook G3 Series" : - "Unknown PowerBook")); + pbook_type[pmu_kind]); + + return via != 0; } void __openfirmware @@ -517,19 +540,25 @@ static void __openfirmware send_byte(int x) { - out_8(&via[ACR], 0x1c); - out_8(&via[SR], x); - out_8(&via[B], via[B] & ~0x10); /* assert TREQ */ + volatile unsigned char *v = via; + + out_8(&v[ACR], in_8(&v[ACR]) | SR_OUT | SR_EXT); + out_8(&v[SR], x); + out_8(&v[B], in_8(&v[B]) & ~TREQ); /* assert TREQ */ } static void __openfirmware recv_byte() { - out_8(&via[ACR], 0x0c); - in_8(&via[SR]); /* resets SR */ - out_8(&via[B], via[B] & ~0x10); + volatile unsigned char *v = via; + + out_8(&v[ACR], (in_8(&v[ACR]) & ~SR_OUT) | SR_EXT); + in_8(&v[SR]); /* resets SR */ + out_8(&v[B], in_8(&v[B]) & ~0x10); } +static int disable_poll; + static void __openfirmware pmu_start() { @@ -549,7 +578,9 @@ data_len = pmu_data_len[req->data[0]][0]; /* set the shift register to shift out and send a byte */ + ++disable_poll; send_byte(req->data[0]); + --disable_poll; out: restore_flags(flags); @@ -560,6 +591,8 @@ { int ie; + if (disable_poll) + return; ie = _disable_interrupts(); if (via[IFR] & (SR_INT | CB1_INT)) via_pmu_interrupt(0, 0, 0); @@ -572,6 +605,7 @@ int intr; int nloop = 0; + ++disable_poll; while ((intr = in_8(&via[IFR])) != 0) { if (++nloop > 1000) { printk(KERN_DEBUG "PMU: stuck in intr loop, " @@ -583,11 +617,9 @@ else if (intr & CB1_INT) { adb_int_pending = 1; out_8(&via[IFR], CB1_INT); - } else - { - /* -- Disabled printk, will happen _really_ often on - PowerBooks ((CB2 interrupts) -- - printk(KERN_DEBUG "PMU: spurrious interrupt intr=%x\n", intr); */ + } + intr &= ~(SR_INT | CB1_INT); + if (intr != 0) { out_8(&via[IFR], intr); } } @@ -600,6 +632,7 @@ pmu_start(); } } + --disable_poll; } static void __openfirmware @@ -608,17 +641,17 @@ struct adb_request *req; int bite, timeout; + if (via[B] & TREQ) { + printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]); + out_8(&via[IFR], SR_INT); + return; + } if (via[B] & TACK) - printk(KERN_DEBUG "PMU: sr_intr but ack still high! (%x)\n", + printk(KERN_ERR "PMU: sr_intr but ack still high! (%x)\n", via[B]); - /* if reading grab the byte, and reset the interrupt */ - if ((via[ACR] & SR_OUT) == 0) - bite = in_8(&via[SR]); - out_8(&via[IFR], SR_INT); - /* reset TREQ and wait for TACK to go high */ - out_8(&via[B], via[B] | TREQ); + out_8(&via[B], in_8(&via[B]) | TREQ); timeout = 3200; while ((in_8(&via[B]) & TACK) == 0) { if (--timeout < 0) { @@ -628,6 +661,11 @@ udelay(10); } + /* if reading grab the byte, and reset the interrupt */ + if (pmu_state == reading || pmu_state == reading_intr) + bite = in_8(&via[SR]); + out_8(&via[IFR], SR_INT); + switch (pmu_state) { case sending: req = current_req; @@ -710,8 +748,6 @@ static void __openfirmware pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs) { - static int show_pmu_ints = 1; - asleep = 0; if (len < 1) { adb_int_pending = 0; @@ -733,12 +769,23 @@ } pmu_done(req); } else { +#ifdef CONFIG_XMON + if (len == 4 && data[1] == 0x2c) { + extern int xmon_wants_key, xmon_pmu_keycode; + if (xmon_wants_key) { + xmon_pmu_keycode = data[2]; + return; + } + } +#endif /* CONFIG_XMON */ /* - * XXX the PMU gives us an up event for keycodes - * 0x74 or 0x75 when the PC card eject buttons - * are released, so we ignore those events. + * XXX On the [23]400 the PMU gives us an up + * event for keycodes 0x74 or 0x75 when the PC + * card eject buttons are released, so we + * ignore those events. */ - if (!(len == 4 && data[1] == 0x2c && data[3] == 0xff + if (!(pmu_kind == PMU_OHARE_BASED && len == 4 + && data[1] == 0x2c && data[3] == 0xff && (data[2] & ~1) == 0xf4)) adb_input(data+1, len-1, regs, 1); } @@ -749,15 +796,6 @@ } else { #ifdef CONFIG_PMAC_PBOOK pmu_pass_intr(data, len); -#else - if (show_pmu_ints - && !(data[0] == PMU_INT_TICK && len == 1)) { - int i; - printk(KERN_DEBUG "pmu intr"); - for (i = 0; i < len; ++i) - printk(" %.2x", data[i]); - printk("\n"); - } #endif } } @@ -773,38 +811,46 @@ struct adb_request req; if (vias == NULL) - return ; + return; - if (on) { - /* first call: get current backlight value */ - if (backlight_level < 0) { - switch(pmu_kind) { - case PMU_OHARE_BASED: + /* first call: get current backlight value */ + if (on && backlight_level < 0) { + switch (pmu_kind) { + case PMU_OHARE_BASED: pmu_request(&req, NULL, 2, 0xd9, 0); while (!req.complete) pmu_poll(); backlight_level = req.reply[1] >> 3; - printk(KERN_DEBUG "pmu: controls returned bright: %d\n", (int)req.reply[1]); break; - case PMU_HEATHROW_BASED: + case PMU_HEATHROW_BASED: + /* We cannot use nvram_read_byte here (not yet initialized) */ pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe); while (!req.complete) pmu_poll(); - printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]); backlight_level = req.reply[1]; + printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", backlight_level); + break; + case PMU_PADDINGTON_BASED: + /* the G3 PB 1999 has a backlight node + and chrp-structured nvram */ + /* XXX should read macos's "blkt" property in nvram + for this node. For now this ensures that the + backlight doesn't go off as soon as linux boots. */ + backlight_level = 20; break; - default: + default: backlight_enabled = 0; return; } - } - pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, - LEVEL_TO_BRIGHT(backlight_level)); - while (!req.complete) - pmu_poll(); + } + if (on) { + pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT, + LEVEL_TO_BRIGHT(backlight_level)); + while (!req.complete) + pmu_poll(); } pmu_request(&req, NULL, 2, PMU_POWER_CTRL, - PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); + PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF)); while (!req.complete) pmu_poll(); backlight_enabled = on; @@ -903,6 +949,68 @@ #ifdef CONFIG_PMAC_PBOOK +static LIST_HEAD(sleep_notifiers); + +int +pmu_register_sleep_notifier(struct pmu_sleep_notifier *n) +{ + struct list_head *list; + struct pmu_sleep_notifier *current; + + for (list = sleep_notifiers.next; list != &sleep_notifiers; + list = list->next) { + current = list_entry(list, struct pmu_sleep_notifier, list); + if (n->priority > current->priority) + break; + } + __list_add(&n->list, list->prev, list); + return 0; +} + +int +pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* n) +{ + if (n->list.next == 0) + return -ENOENT; + list_del(&n->list); + n->list.next = 0; + return 0; +} + +/* Sleep is broadcast last-to-first */ +static int +broadcast_sleep(int when, int can_cancel) +{ + int ret = PBOOK_SLEEP_OK; + struct list_head *list; + struct pmu_sleep_notifier *current; + + for (list = sleep_notifiers.prev; list != &sleep_notifiers; + list = list->prev) { + current = list_entry(list, struct pmu_sleep_notifier, list); + ret = current->notifier_call(current, when); + if (can_cancel && (ret != PBOOK_SLEEP_OK)) + return ret; + } + return ret; +} + +/* Wake is broadcast first-to-last */ +static int +broadcast_wake(void) +{ + int ret = PBOOK_SLEEP_OK; + struct list_head *list; + struct pmu_sleep_notifier *current; + + for (list = sleep_notifiers.next; list != &sleep_notifiers; + list = list->next) { + current = list_entry(list, struct pmu_sleep_notifier, list); + current->notifier_call(current, PBOOK_WAKE); + } + return ret; +} + /* * This struct is used to store config register values for * PCI devices which may get powered off when we sleep. @@ -914,7 +1022,7 @@ } *pbook_pci_saves; static int n_pbook_pci_saves; -static inline void __openfirmware +static void __openfirmware pbook_pci_save(void) { int npci; @@ -941,7 +1049,7 @@ } } -static inline void __openfirmware +static void __openfirmware pbook_pci_restore(void) { u16 cmd; @@ -974,46 +1082,168 @@ } } +#if 0 +/* N.B. This doesn't work on the 3400 */ +void pmu_blink(int n) +{ + struct adb_request req; + + for (; n > 0; --n) { + pmu_request(&req, NULL, 4, 0xee, 4, 0, 1); + while (!req.complete) pmu_poll(); + udelay(50000); + pmu_request(&req, NULL, 4, 0xee, 4, 0, 0); + while (!req.complete) pmu_poll(); + udelay(50000); + } + udelay(50000); +} +#endif + /* * Put the powerbook to sleep. */ -#define IRQ_ENABLE ((unsigned int *)0xf3000024) -#define MEM_CTRL ((unsigned int *)0xf8000070) + +#define FEATURE_CTRL(base) ((unsigned int *)(base + 0x38)) +#define GRACKLE_PM (1<<7) +#define GRACKLE_DOZE (1<<5) +#define GRACKLE_NAP (1<<4) +#define GRACKLE_SLEEP (1<<3) + +int __openfirmware powerbook_sleep_G3(void) +{ + int ret; + unsigned long save_l2cr; + unsigned long save_fcr; + unsigned long wait; + unsigned short pmcr1; + struct adb_request sleep_req; + struct device_node *macio; + unsigned long macio_base = 0; + + macio = find_devices("mac-io"); + if (macio != 0 && macio->n_addrs > 0) + macio_base = (unsigned long) + ioremap(macio->addrs[0].address, 0x40); + + /* Sync the disks. */ + /* XXX It would be nice to have some way to ensure that + * nobody is dirtying any new buffers while we wait. */ + fsync_dev(0); + + /* Notify device drivers */ + ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, 1); + if (ret != PBOOK_SLEEP_OK) { + broadcast_sleep(PBOOK_SLEEP_REJECT, 0); + printk("pmu: sleep rejected\n"); + return -EBUSY; + } + broadcast_sleep(PBOOK_SLEEP_NOW, 0); + + /* Give the disks a little time to actually finish writing */ + for (wait = jiffies + (HZ/4); time_before(jiffies, wait); ) + mb(); + + /* Disable all interrupts except pmu */ + sleep_save_intrs(vias->intrs[0].line); -int __openfirmware powerbook_sleep(void) + /* Make sure the decrementer won't interrupt us */ + asm volatile("mtdec %0" : : "r" (0x7fffffff)); +#if 0 + /* Save the state of PCI config space for some slots */ + pbook_pci_save(); +#endif + /* For 750, save backside cache setting and disable it */ + save_l2cr = _get_L2CR(); /* (returns 0 if not 750) */ + if (save_l2cr) + _set_L2CR(0); + + if (macio_base != 0) { + save_fcr = in_le32(FEATURE_CTRL(macio_base)); + /* Check if this is still valid on older powerbooks */ + out_le32(FEATURE_CTRL(macio_base), save_fcr & ~(0x00000140UL)); + } + + if (current->tss.regs && (current->tss.regs->msr & MSR_FP) != 0) + giveup_fpu(current); + + grackle_pcibios_read_config_word(0,0,0x70,&pmcr1); + /* Apparently, MacOS uses NAP mode for Grackle ??? */ + pmcr1 &= ~(GRACKLE_DOZE|GRACKLE_SLEEP); + pmcr1 |= GRACKLE_PM|GRACKLE_NAP; + grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1); + + /* Ask the PMU to put us to sleep */ + pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); + while (!sleep_req.complete) + mb(); + + cli(); + while (pmu_state != idle) + pmu_poll(); + + /* Call low-level ASM sleep handler */ + low_sleep_handler(); + + /* We're awake again, stop grackle PM */ + grackle_pcibios_read_config_word(0, 0, 0x70, &pmcr1); + pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); + grackle_pcibios_write_config_word(0, 0, 0x70, pmcr1); + + sti(); +#if 0 + /* According to someone from Apple, this should not be needed, + at least not for all devices. Let's keep it for now until we + have something that works. */ + pbook_pci_restore(); +#endif + set_context(current->mm->context); + + /* Restore L2 cache */ + if (save_l2cr) + _set_L2CR(save_l2cr | 0x200000); /* set invalidate bit */ + + /* reenable interrupts */ + sleep_restore_intrs(); + + /* Notify drivers */ + broadcast_wake(); + + return 0; +} + +#define PB3400_MEM_CTRL ((unsigned int *)0xf8000070) + +int __openfirmware powerbook_sleep_3400(void) { int ret, i, x; - static int save_backlight; - static unsigned int save_irqen; unsigned long msr; unsigned int hid0; unsigned long p, wait; struct adb_request sleep_req; - /* Notify device drivers */ - ret = notifier_call_chain(&sleep_notifier_list, PBOOK_SLEEP, NULL); - if (ret & NOTIFY_STOP_MASK) - return -EBUSY; - /* Sync the disks. */ /* XXX It would be nice to have some way to ensure that * nobody is dirtying any new buffers while we wait. */ fsync_dev(0); - /* Turn off the display backlight */ - save_backlight = backlight_enabled; - if (save_backlight) - pmu_enable_backlight(0); + /* Notify device drivers */ + ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, 1); + if (ret != PBOOK_SLEEP_OK) { + broadcast_sleep(PBOOK_SLEEP_REJECT, 0); + printk("pmu: sleep rejected\n"); + return -EBUSY; + } + broadcast_sleep(PBOOK_SLEEP_NOW, 0); /* Give the disks a little time to actually finish writing */ for (wait = jiffies + (HZ/4); time_before(jiffies, wait); ) mb(); /* Disable all interrupts except pmu */ - save_irqen = in_le32(IRQ_ENABLE); - for (i = 0; i < 32; ++i) - if (i != vias->intrs[0].line && (save_irqen & (1 << i))) - disable_irq(i); + sleep_save_intrs(vias->intrs[0].line); + + /* Make sure the decrementer won't interrupt us */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* Save the state of PCI config space for some slots */ @@ -1022,9 +1252,9 @@ /* Set the memory controller to keep the memory refreshed while we're asleep */ for (i = 0x403f; i >= 0x4000; --i) { - out_be32(MEM_CTRL, i); + out_be32(PB3400_MEM_CTRL, i); do { - x = (in_be32(MEM_CTRL) >> 16) & 0x3ff; + x = (in_be32(PB3400_MEM_CTRL) >> 16) & 0x3ff; } while (x == 0); if (x >= 0x100) break; @@ -1034,6 +1264,7 @@ pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T'); while (!sleep_req.complete) mb(); + /* displacement-flush the L2 cache - necessary? */ for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000) i = *(volatile int *)p; @@ -1049,7 +1280,7 @@ udelay(10); /* OK, we're awake again, start restoring things */ - out_be32(MEM_CTRL, 0x3f); + out_be32(PB3400_MEM_CTRL, 0x3f); pbook_pci_restore(); /* wait for the PMU interrupt sequence to complete */ @@ -1057,21 +1288,10 @@ mb(); /* reenable interrupts */ - for (i = 0; i < 32; ++i) - if (i != vias->intrs[0].line && (save_irqen & (1 << i))) - enable_irq(i); + sleep_restore_intrs(); /* Notify drivers */ - notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL); - - /* reenable ADB autopoll */ - pmu_adb_autopoll(adb_dev_map); - - /* Turn on the screen backlight, if it was on before */ - if (save_backlight) - pmu_enable_backlight(1); - - /* Wait for the hard disk to spin up */ + broadcast_wake(); return 0; } @@ -1224,7 +1444,7 @@ } /* Note: removed __openfirmware here since it causes link errors */ -static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp, +static int pmu_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) { int error; @@ -1232,9 +1452,18 @@ switch (cmd) { case PMU_IOC_SLEEP: - if (pmu_kind != PMU_OHARE_BASED) - return -ENOSYS; - return powerbook_sleep(); + switch (pmu_kind) { + case PMU_OHARE_BASED: + error = powerbook_sleep_3400(); + break; + case PMU_HEATHROW_BASED: + case PMU_PADDINGTON_BASED: + error = powerbook_sleep_G3(); + break; + default: + error = ENOSYS; + } + return error; case PMU_IOC_GET_BACKLIGHT: return put_user(backlight_level, (__u32 *)arg); case PMU_IOC_SET_BACKLIGHT: @@ -1272,3 +1501,72 @@ } #endif /* CONFIG_PMAC_PBOOK */ +#if 0 +static inline void polled_handshake(volatile unsigned char *via) +{ + via[B] &= ~TREQ; eieio(); + while ((via[B] & TACK) != 0) + ; + via[B] |= TREQ; eieio(); + while ((via[B] & TACK) == 0) + ; +} + +static inline void polled_send_byte(volatile unsigned char *via, int x) +{ + xmon_printf("s%.2x", x); + via[ACR] |= SR_OUT | SR_EXT; eieio(); + via[SR] = x; eieio(); + polled_handshake(via); +} + +static inline int polled_recv_byte(volatile unsigned char *via) +{ + int x; + + via[ACR] = (via[ACR] & ~SR_OUT) | SR_EXT; eieio(); + x = via[SR]; eieio(); + polled_handshake(via); + x = via[SR]; eieio(); + xmon_printf("r%.2x", x); + return x; +} + +int +pmu_polled_request(struct adb_request *req) +{ + unsigned long flags; + int i, l, c; + volatile unsigned char *v = via; + + req->complete = 1; + c = req->data[0]; + l = pmu_data_len[c][0]; + if (l >= 0 && req->nbytes != l + 1) + return -EINVAL; + + save_flags(flags); cli(); + while (pmu_state != idle) + pmu_poll(); + + polled_send_byte(v, c); + if (l < 0) { + l = req->nbytes - 1; + polled_send_byte(v, l); + } + for (i = 1; i <= l; ++i) + polled_send_byte(v, req->data[i]); + + l = pmu_data_len[c][1]; + if (l < 0) + l = polled_recv_byte(v); + for (i = 0; i < l; ++i) + req->reply[i + req->reply_len] = polled_recv_byte(v); + + if (req->done) + (*req->done)(req); + + restore_flags(flags); + return 0; +} +#endif /* 0 */ diff -u --recursive --new-file v2.2.11/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.2.11/linux/drivers/net/Config.in Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/Config.in Wed Aug 25 17:29:48 1999 @@ -231,6 +231,11 @@ endmenu +bool 'Fibre Channel driver support' CONFIG_NET_FC +if [ "$CONFIG_NET_FC" = "y" ]; then + tristate 'Interphase 5526 Tachyon chipset based adaptor support' CONFIG_IPHASE5526 +fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Red Creek Hardware VPN (EXPERIMENTAL)' CONFIG_RCPCI tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER diff -u --recursive --new-file v2.2.11/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.2.11/linux/drivers/net/Makefile Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/Makefile Wed Aug 25 17:29:48 1999 @@ -5,7 +5,7 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda +ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc L_TARGET := net.a L_OBJS := auto_irq.o @@ -1135,6 +1135,15 @@ else ifeq ($(CONFIG_IRDA),m) MOD_IN_SUB_DIRS += irda + endif +endif + +ifeq ($(CONFIG_NET_FC),y) +SUB_DIRS += fc +MOD_IN_SUB_DIRS += fc +else + ifeq ($(CONFIG_NET_FC),m) + MOD_IN_SUB_DIRS += fc endif endif diff -u --recursive --new-file v2.2.11/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.2.11/linux/drivers/net/Space.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/Space.c Wed Aug 25 17:29:48 1999 @@ -136,6 +136,9 @@ /* HIPPI boards */ extern int rr_hippi_probe(struct device *); +/* Fibre Channel adapters */ +extern int iph5526_probe(struct device *dev); + struct devprobe { int (*probe)(struct device *dev); @@ -574,6 +577,25 @@ } #endif + +#ifdef CONFIG_NET_FC +static int fcif_probe(struct device *dev) +{ + if (dev->base_addr == -1) + return 1; + + if (1 +#ifdef CONFIG_IPHASE5526 + && iph5526_probe(dev) +#endif + && 1 ) { + return 1; /* -ENODEV or -EAGAIN would be more accurate. */ + } + return 0; +} +#endif /* CONFIG_NET_FC */ + + #ifdef CONFIG_ETHERTAP static struct device tap0_dev = { "tap0", 0, 0, 0, 0, NETLINK_TAPBASE, 0, 0, 0, 0, NEXT_DEV, ethertap_probe, }; # undef NEXT_DEV @@ -821,9 +843,20 @@ # undef NEXT_DEV # define NEXT_DEV (&bif_dev) #endif + + +#ifdef CONFIG_NET_FC + static struct device fc1_dev = { + "fc1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, fcif_probe}; + static struct device fc0_dev = { + "fc0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fc1_dev, fcif_probe}; +# undef NEXT_DEV +# define NEXT_DEV (&fc0_dev) +#endif + #ifdef CONFIG_NET_SB1000 - extern int sb1000_init(struct device *dev); + extern int sb1000_probe(struct device *dev); static struct device sb1000_dev = { "cm0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, sb1000_probe }; # undef NEXT_DEV diff -u --recursive --new-file v2.2.11/linux/drivers/net/acenic.c linux/drivers/net/acenic.c --- v2.2.11/linux/drivers/net/acenic.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/acenic.c Wed Aug 25 17:29:48 1999 @@ -166,10 +166,10 @@ * Default values for tuning parameters */ #define DEF_TX_RATIO 31 -#define DEF_TX_COAL TICKS_PER_SEC / 500 -#define DEF_TX_MAX_DESC 7 -#define DEF_RX_COAL TICKS_PER_SEC / 10000 -#define DEF_RX_MAX_DESC 2 +#define DEF_TX_COAL 1000 +#define DEF_TX_MAX_DESC 40 +#define DEF_RX_COAL 1000 +#define DEF_RX_MAX_DESC 20 #define DEF_TRACE 0 #define DEF_STAT 2 * TICKS_PER_SEC @@ -181,7 +181,7 @@ static int max_rx_desc[8] = {0, }; static int tx_ratio[8] = {0, }; -static const char __initdata *version = "acenic.c: v0.33 07/20/99 Jes Sorensen (Jes.Sorensen@cern.ch)\n"; +static const char __initdata *version = "acenic.c: v0.33a 08/16/99 Jes Sorensen (Jes.Sorensen@cern.ch)\n"; static struct device *root_dev = NULL; diff -u --recursive --new-file v2.2.11/linux/drivers/net/bmac.c linux/drivers/net/bmac.c --- v2.2.11/linux/drivers/net/bmac.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/bmac.c Wed Aug 25 17:29:48 1999 @@ -19,6 +19,11 @@ #include #include #include +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#include +#endif #include "bmac.h" #define trunc_page(x) ((void *)(((unsigned long)(x)) & ~((unsigned long)(PAGE_SIZE - 1)))) @@ -117,6 +122,13 @@ struct device *bmac_devs = NULL; static int is_bmac_plus; +#ifdef CONFIG_PMAC_PBOOK +int bmac_sleep_notify(struct pmu_sleep_notifier *self, int when); +static struct pmu_sleep_notifier bmac_sleep_notifier = { + bmac_sleep_notify, SLEEP_LEVEL_NET, +}; +#endif + #if 0 /* * If we can't get a skbuff when we need it, we use this area for DMA. @@ -244,7 +256,7 @@ udelay(10000); } -#define MIFDELAY udelay(500) +#define MIFDELAY udelay(10) static unsigned int bmac_mif_readbits(struct device *dev, int nb) @@ -427,8 +439,8 @@ udelay(20000); } -static int -bmac_init_chip(struct device *dev) +static void +bmac_init_phy(struct device *dev) { unsigned int addr; @@ -451,10 +463,54 @@ } else bmac_mif_write(dev, 0, 0x1000); } +} + +static int +bmac_init_chip(struct device *dev) +{ + bmac_init_phy(dev); bmac_init_registers(dev); return 1; } +#ifdef CONFIG_PMAC_PBOOK +int +bmac_sleep_notify(struct pmu_sleep_notifier *self, int when) +{ + struct bmac_data *bp; + + if (bmac_devs == 0) + return PBOOK_SLEEP_OK; + + bp = (struct bmac_data *) bmac_devs->priv; + + switch (when) { + case PBOOK_SLEEP_REQUEST: + break; + case PBOOK_SLEEP_REJECT: + break; + case PBOOK_SLEEP_NOW: + /* prolly should wait for dma to finish & turn off the chip */ + disable_irq(bmac_devs->irq); + disable_irq(bp->tx_dma_intr); + disable_irq(bp->rx_dma_intr); + feature_set(bp->node, FEATURE_BMac_reset); + udelay(10000); + feature_clear(bp->node, FEATURE_BMac_IO_enable); + udelay(10000); + break; + case PBOOK_WAKE: + /* see if this is enough */ + bmac_reset_and_enable(bmac_devs, 1); + enable_irq(bmac_devs->irq); + enable_irq(bp->tx_dma_intr); + enable_irq(bp->rx_dma_intr); + break; + } + return PBOOK_SLEEP_OK; +} +#endif + static int bmac_set_address(struct device *dev, void *addr) { unsigned char *p = addr; @@ -1220,7 +1276,12 @@ if (bmacs == NULL) return -ENODEV; next_bmac = bmacs->next; - bmac_devs = dev; /* KLUDGE!! */ + if (bmac_devs == 0) { + bmac_devs = dev; /* KLUDGE!! */ +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&bmac_sleep_notifier); +#endif + } if (bmacs->n_addrs != 3 || bmacs->n_intrs != 3) { printk(KERN_ERR "can't use BMAC %s: expect 3 addrs and 3 intrs\n", @@ -1556,15 +1617,24 @@ res = bmac_probe(NULL); return res; } + void cleanup_module(void) { - struct bmac_data *bp = (struct bmac_data *) bmac_devs->priv; + struct bmac_data *bp; + + if (bmac_devs == 0) + return; + + bp = (struct bmac_data *) bmac_devs->priv; unregister_netdev(bmac_devs); free_irq(bmac_devs->irq, bmac_misc_intr); free_irq(bp->tx_dma_intr, bmac_txdma_intr); free_irq(bp->rx_dma_intr, bmac_rxdma_intr); +#ifdef CONFIG_PMAC_PBOOK + pmu_unregister_sleep_notifier(&bmac_sleep_notifier); +#endif kfree(bmac_devs); bmac_devs = NULL; } diff -u --recursive --new-file v2.2.11/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c --- v2.2.11/linux/drivers/net/eth16i.c Thu Jan 7 08:46:59 1999 +++ linux/drivers/net/eth16i.c Wed Aug 25 17:29:48 1999 @@ -886,7 +886,7 @@ creg[0] &= 0x0F; /* Mask collision cnr */ creg[2] &= 0x7F; /* Mask DCLEN bit */ -#ifdef 0 +#if 0 /* This was removed because the card was sometimes left to state from which it couldn't be find anymore. If there is need diff -u --recursive --new-file v2.2.11/linux/drivers/net/fc/Makefile linux/drivers/net/fc/Makefile --- v2.2.11/linux/drivers/net/fc/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/fc/Makefile Wed Aug 25 17:29:48 1999 @@ -0,0 +1,28 @@ + +# Makefile for linux/drivers/net/fc +# +# 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). +# + +L_TARGET := fc.a +L_OBJS := +M_OBJS := +MX_OBJS := +MOD_LIST_NAME := FC_MODULES +FC_SRCS = $(wildcard $(L_OBJS:%.o=%.c)) + +ifeq ($(CONFIG_IPHASE5526),y) +L_OBJS += iph5526.o +else + ifeq ($(CONFIG_IPHASE5526),m) + M_OBJS += iph5526.o + endif +endif + +include $(TOPDIR)/Rules.make + +clean: + rm *.o + diff -u --recursive --new-file v2.2.11/linux/drivers/net/fc/iph5526.c linux/drivers/net/fc/iph5526.c --- v2.2.11/linux/drivers/net/fc/iph5526.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/fc/iph5526.c Wed Aug 25 17:29:48 1999 @@ -0,0 +1,4703 @@ +/********************************************************************** + * iph5526.c: IP/SCSI driver for the Interphase 5526 PCI Fibre Channel + * Card. + * Copyright (C) 1999 Vineet M Abraham + * + * 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. + *********************************************************************/ +/********************************************************************** +Log: +Vineet M Abraham +02.12.99 Support multiple cards. +03.15.99 Added Fabric support. +04.04.99 Added N_Port support. +04.15.99 Added SCSI support. +06.18.99 Added ABTS Protocol. +06.24.99 Fixed data corruption when multiple XFER_RDYs are received. +07.07.99 Can be loaded as part of the Kernel. Changed semaphores. Added + more checks before invalidating SEST entries. +07.08.99 Added Broadcast IP stuff and fixed an unicast timeout bug. +***********************************************************************/ +/* TODO: + R_T_TOV set to 15msec in Loop topology. Need to be 100 msec. + SMP testing. + Fix ADISC Tx before completing FLOGI. +*/ + +static const char *version = + "iph5526.c:v1.0 07.08.99 Vineet Abraham (vma@iol.unh.edu)\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* had the declarations for init_fcdev among others + includes if_fcdevice.h */ + +#include +#include "../../scsi/sd.h" +#include "../../scsi/scsi.h" +#include "../../scsi/hosts.h" +#include "../../fc4/fcp.h" + +/* driver specific header files */ +#include "tach.h" +#include "tach_structs.h" +#include "iph5526_ip.h" +#include "iph5526_scsi.h" +#include "iph5526_novram.c" + +#define RUN_AT(x) (jiffies + (x)) + +#define DEBUG_5526_0 0 +#define DEBUG_5526_1 0 +#define DEBUG_5526_2 0 + +#if DEBUG_5526_0 +#define DPRINTK(format, a...) {printk("%s: ", fi->name); \ + printk(format, ##a); \ + printk("\n");} +#define ENTER(x) {printk("%s: ", fi->name); \ + printk("iph5526.c : entering %s()\n", x);} +#define LEAVE(x) {printk("%s: ", fi->name); \ + printk("iph5526.c : leaving %s()\n",x);} + +#else +#define DPRINTK(format, a...) {} +#define ENTER(x) {} +#define LEAVE(x) {} +#endif + +#if DEBUG_5526_1 +#define DPRINTK1(format, a...) {printk("%s: ", fi->name); \ + printk(format, ##a); \ + printk("\n");} +#else +#define DPRINTK1(format, a...) {} +#endif + +#if DEBUG_5526_2 +#define DPRINTK2(format, a...) {printk("%s: ", fi->name); \ + printk(format, ##a); \ + printk("\n");} +#else +#define DPRINTK2(format, a...) {} +#endif + +#define T_MSG(format, a...) {printk("%s: ", fi->name); \ + printk(format, ##a);\ + printk("\n");} + +#define ALIGNED_SFS_ADDR(addr) ((((unsigned long)(addr) + (SFS_BUFFER_SIZE - 1)) & ~(SFS_BUFFER_SIZE - 1)) - (unsigned long)(addr)) +#define ALIGNED_ADDR(addr, len) ((((unsigned long)(addr) + (len - 1)) & ~(len - 1)) - (unsigned long)(addr)) + + +#define MAX_FC_CARDS 2 +static struct fc_info *fc[MAX_FC_CARDS+1]; +static unsigned int pci_irq_line = 0; +static struct { + unsigned short vendor_id; + unsigned short device_id; + char *name; +} +clone_list[] __initdata = { + {PCI_VENDOR_ID_INTERPHASE, PCI_DEVICE_ID_INTERPHASE_5526, "Interphase Fibre Channel HBA"}, + {PCI_VENDOR_ID_INTERPHASE, PCI_DEVICE_ID_INTERPHASE_55x6, "Interphase Fibre Channel HBA"}, + {0,} +}; + +static void tachyon_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void tachyon_interrupt_handler(int irq, void* dev_id, struct pt_regs* regs); + +static int initialize_register_pointers(struct fc_info *fi); +void clean_up_memory(struct fc_info *fi); + +static int tachyon_init(struct fc_info *fi); +static int build_queues(struct fc_info *fi); +static void build_tachyon_header(struct fc_info *fi, u_int my_id, u_int r_ctl, u_int d_id, u_int type, u_char seq_id, u_char df_ctl, u_short ox_id, u_short rx_id, char *data); +static int get_free_header(struct fc_info *fi); +static void build_EDB(struct fc_info *fi, char *data, u_short flags, u_short len); +static int get_free_EDB(struct fc_info *fi); +static void build_ODB(struct fc_info *fi, u_char seq_id, u_int d_id, u_int len, u_int cntl, u_short mtu, u_short ox_id, u_short rx_id, int NW_header, int int_required, u_int frame_class); +static void write_to_tachyon_registers(struct fc_info *fi); +static void reset_latch(struct fc_info *fi); +static void reset_tachyon(struct fc_info *fi, u_int value); +static void take_tachyon_offline(struct fc_info *fi); +static void read_novram(struct fc_info *fi); +static void reset_ichip(struct fc_info *fi); +static void update_OCQ_indx(struct fc_info *fi); +static void update_IMQ_indx(struct fc_info *fi, int count); +static void update_SFSBQ_indx(struct fc_info *fi); +static void update_MFSBQ_indx(struct fc_info *fi, int count); +static void update_tachyon_header_indx(struct fc_info *fi); +static void update_EDB_indx(struct fc_info *fi); +static void handle_FM_interrupt(struct fc_info *fi); +static void handle_MFS_interrupt(struct fc_info *fi); +static void handle_OOO_interrupt(struct fc_info *fi); +static void handle_SFS_interrupt(struct fc_info *fi); +static void handle_OCI_interrupt(struct fc_info *fi); +static void handle_SFS_BUF_WARN_interrupt(struct fc_info *fi); +static void handle_MFS_BUF_WARN_interrupt(struct fc_info *fi); +static void handle_IMQ_BUF_WARN_interrupt(struct fc_info *fi); +static void handle_Unknown_Frame_interrupt(struct fc_info *fi); +static void handle_Busied_Frame_interrupt(struct fc_info *fi); +static void handle_Bad_SCSI_Frame_interrupt(struct fc_info *fi); +static void handle_Inbound_SCSI_Status_interrupt(struct fc_info *fi); +static void handle_Inbound_SCSI_Command_interrupt(struct fc_info *fi); +static void completion_message_handler(struct fc_info *fi, u_int imq_int_type); +static void fill_login_frame(struct fc_info *fi, u_int logi); + +static int tx_exchange(struct fc_info *fi, char *data, u_int len, u_int r_ctl, u_int type, u_int d_id, u_int mtu, int int_required, u_short ox_id, u_int frame_class); +static int tx_sequence(struct fc_info *fi, char *data, u_int len, u_int mtu, u_int d_id, u_short ox_id, u_short rx_id, u_char seq_id, int NW_flag, int int_required, u_int frame_class); +static int validate_login(struct fc_info *fi, u_int *base_ptr); +static void add_to_address_cache(struct fc_info *fi, u_int *base_ptr); +static void remove_from_address_cache(struct fc_info *fi, u_int *data, u_int cmnd_code); +static int node_logged_in_prev(struct fc_info *fi, u_int *buff_addr); +static int sid_logged_in(struct fc_info *fi, u_int s_id); +static struct fc_node_info *look_up_cache(struct fc_info *fi, char *data); +static int display_cache(struct fc_info *fi); + +static void tx_logi(struct fc_info *fi, u_int logi, u_int d_id); +static void tx_logi_acc(struct fc_info *fi, u_int logi, u_int d_id, u_short received_ox_id); +static void tx_prli(struct fc_info *fi, u_int command_code, u_int d_id, u_short received_ox_id); +static void tx_logo(struct fc_info *fi, u_int d_id, u_short received_ox_id); +static void tx_adisc(struct fc_info *fi, u_int cmnd_code, u_int d_id, u_short received_ox_id); +static void tx_ls_rjt(struct fc_info *fi, u_int d_id, u_short received_ox_id, u_short reason_code, u_short expln_code); +static u_int plogi_ok(struct fc_info *fi, u_int *buff_addr, int size); +static void tx_acc(struct fc_info *fi, u_int d_id, u_short received_ox_id); +static void tx_name_server_req(struct fc_info *fi, u_int req); +static void rscn_handler(struct fc_info *fi, u_int node_id); +static void tx_scr(struct fc_info *fi); +static void scr_timer(unsigned long data); +static void explore_fabric(struct fc_info *fi, u_int *buff_addr); +static void perform_adisc(struct fc_info *fi); +static void local_port_discovery(struct fc_info *fi); +static void add_to_ox_id_list(struct fc_info *fi, u_int transaction_id, u_int cmnd_code); +static u_int remove_from_ox_id_list(struct fc_info *fi, u_short received_ox_id); +static void add_display_cache_timer(struct fc_info *fi); + +/* Timers... */ +static void nos_ols_timer(unsigned long data); +static void loop_timer(unsigned long data); +static void fabric_explore_timer(unsigned long data); +static void port_discovery_timer(unsigned long data); +static void display_cache_timer(unsigned long data); + +/* SCSI Stuff */ +static int add_to_sest(struct fc_info *fi, Scsi_Cmnd *Cmnd, struct fc_node_info *ni); +static struct fc_node_info *resolve_target(struct fc_info *fi, u_char target); +static void update_FCP_CMND_indx(struct fc_info *fi); +static int get_free_SDB(struct fc_info *fi); +static void update_SDB_indx(struct fc_info *fi); +static void mark_scsi_sid(struct fc_info *fi, u_int *buff_addr, u_char action); +static void invalidate_SEST_entry(struct fc_info *fi, u_short received_ox_id); +static int abort_exchange(struct fc_info *fi, u_short ox_id); +static void flush_tachyon_cache(struct fc_info *fi, u_short ox_id); +static int get_scsi_oxid(struct fc_info *fi); +static void update_scsi_oxid(struct fc_info *fi); + +Scsi_Host_Template driver_template = IPH5526_SCSI_FC; + + +#ifdef CONFIG_PCI +static int iph5526_probe_pci(struct device *dev); +#endif + + +__initfunc(int iph5526_probe(struct device *dev)) +{ +#ifdef CONFIG_PCI + if (pci_present() && (iph5526_probe_pci(dev) == 0)) + return 0; +#endif + return -ENODEV; +} + +#ifdef CONFIG_PCI +__initfunc(static int iph5526_probe_pci(struct device *dev)) +{ +#ifndef MODULE +struct fc_info *fi; +static int count = 0; +#endif +#ifdef MODULE +struct fc_info *fi = (struct fc_info *)dev->priv; +#endif + +#ifndef MODULE + if(fc[count] != NULL) { + if (dev == NULL) { + dev = init_fcdev(NULL, 0); + if (dev == NULL) + return -ENOMEM; + } + fi = fc[count]; +#endif + fi->dev = dev; + dev->base_addr = fi->base_addr; + dev->irq = fi->irq; + if (dev->priv == NULL) + dev->priv = fi; + fcdev_init(dev); + /* Assign ur MAC address. + */ + dev->dev_addr[0] = (fi->g.my_port_name_high & 0x0000FF00) >> 8; + dev->dev_addr[1] = fi->g.my_port_name_high; + dev->dev_addr[2] = (fi->g.my_port_name_low & 0xFF000000) >> 24; + dev->dev_addr[3] = (fi->g.my_port_name_low & 0x00FF0000) >> 16; + dev->dev_addr[4] = (fi->g.my_port_name_low & 0x0000FF00) >> 8; + dev->dev_addr[5] = fi->g.my_port_name_low; +#ifndef MODULE + count++; + } + else + return -ENODEV; +#endif + display_cache(fi); + return 0; +} +#endif /* CONFIG_PCI */ + +__initfunc(static int fcdev_init(struct device *dev)) +{ + dev->open = iph5526_open; + dev->stop = iph5526_close; + dev->hard_start_xmit = iph5526_send_packet; + dev->get_stats = iph5526_get_stats; + dev->set_multicast_list = NULL; + dev->change_mtu = iph5526_change_mtu; +#ifndef MODULE + fc_setup(dev); +#endif + return 0; +} + +/* initialize tachyon and take it OnLine */ +static int tachyon_init(struct fc_info *fi) +{ + ENTER("tachyon_init"); + if (build_queues(fi) == 0) { + T_MSG("build_queues() failed"); + return 0; + } + + /* Retrieve your port/node name. + */ + read_novram(fi); + + reset_ichip(fi); + + reset_tachyon(fi, SOFTWARE_RESET); + + LEAVE("tachyon_init"); + return 1; +} + +/* Build the 4 Qs - IMQ, OCQ, MFSBQ, SFSBQ */ +/* Lots of dma_pages needed as Tachyon DMAs almost everything into + * host memory. + */ +static int build_queues(struct fc_info *fi) +{ +int i,j; +u_char *addr; + ENTER("build_queues"); + /* Initializing Queue Variables. + */ + fi->q.ptr_host_ocq_cons_indx = NULL; + fi->q.ptr_host_hpcq_cons_indx = NULL; + fi->q.ptr_host_imq_prod_indx = NULL; + + fi->q.ptr_ocq_base = NULL; + fi->q.ocq_len = 0; + fi->q.ocq_end = 0; + fi->q.ocq_prod_indx = 0; + + fi->q.ptr_imq_base = NULL; + fi->q.imq_len = 0; + fi->q.imq_end = 0; + fi->q.imq_cons_indx = 0; + fi->q.imq_prod_indx = 0; + + fi->q.ptr_mfsbq_base = NULL; + fi->q.mfsbq_len = 0; + fi->q.mfsbq_end = 0; + fi->q.mfsbq_prod_indx = 0; + fi->q.mfsbq_cons_indx = 0; + fi->q.mfsbuff_len = 0; + fi->q.mfsbuff_end = 0; + fi->g.mfs_buffer_count = 0; + + fi->q.ptr_sfsbq_base = NULL; + fi->q.sfsbq_len = 0; + fi->q.sfsbq_end = 0; + fi->q.sfsbq_prod_indx = 0; + fi->q.sfsbq_cons_indx = 0; + fi->q.sfsbuff_len = 0; + fi->q.sfsbuff_end = 0; + + fi->q.sdb_indx = 0; + fi->q.fcp_cmnd_indx = 0; + + fi->q.ptr_edb_base = NULL; + fi->q.edb_buffer_indx = 0; + fi->q.ptr_tachyon_header_base = NULL; + fi->q.tachyon_header_indx = 0; + fi->node_info_list = NULL; + fi->ox_id_list = NULL; + fi->g.loop_up = FALSE; + fi->g.ptp_up = FALSE; + fi->g.link_up = FALSE; + fi->g.fabric_present = FALSE; + fi->g.n_port_try = FALSE; + fi->g.dont_init = FALSE; + fi->g.nport_timer_set = FALSE; + fi->g.lport_timer_set = FALSE; + fi->g.no_of_targets = 0; + fi->g.sem = 0; + fi->g.perform_adisc = FALSE; + fi->g.e_i = 0; + + /* build OCQ */ + if ( (fi->q.ptr_ocq_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get OCQ page"); + return 0; + } + /* set up the OCQ structures */ + for (i = 0; i < OCQ_LENGTH; i++) + fi->q.ptr_odb[i] = fi->q.ptr_ocq_base + NO_OF_ENTRIES*i; + + /* build IMQ */ + if ( (fi->q.ptr_imq_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get IMQ page"); + return 0; + } + for (i = 0; i < IMQ_LENGTH; i++) + fi->q.ptr_imqe[i] = fi->q.ptr_imq_base + NO_OF_ENTRIES*i; + + /* build MFSBQ */ + if ( (fi->q.ptr_mfsbq_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get MFSBQ page"); + return 0; + } + memset((char *)fi->q.ptr_mfsbq_base, 0, MFSBQ_LENGTH * 32); + /* Allocate one huge chunk of memory... helps while reassembling + * frames. + */ + if ( (addr = (u_char *)__get_free_pages(GFP_KERNEL, 5) ) == 0) { + T_MSG("failed to get MFSBQ page"); + return 0; + } + /* fill in addresses of empty buffers */ + for (i = 0; i < MFSBQ_LENGTH; i++) { + for (j = 0; j < NO_OF_ENTRIES; j++) { + *(fi->q.ptr_mfsbq_base + i*NO_OF_ENTRIES + j) = htonl(virt_to_bus(addr)); + addr += MFS_BUFFER_SIZE; + } + } + + /* The number of entries in each MFS buffer is 8. There are 8 + * MFS buffers. That leaves us with 4096-256 bytes. We use them + * as temporary space for ELS frames. This is done to make sure that + * the addresses are aligned. + */ + fi->g.els_buffer[0] = fi->q.ptr_mfsbq_base + MFSBQ_LENGTH*NO_OF_ENTRIES; + for (i = 1; i < MAX_PENDING_FRAMES; i++) + fi->g.els_buffer[i] = fi->g.els_buffer[i-1] + 64; + + /* build SFSBQ */ + if ( (fi->q.ptr_sfsbq_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get SFSBQ page"); + return 0; + } + memset((char *)fi->q.ptr_sfsbq_base, 0, SFSBQ_LENGTH * 32); + /* fill in addresses of empty buffers */ + for (i = 0; i < SFSBQ_LENGTH; i++) + for (j = 0; j < NO_OF_ENTRIES; j++){ + addr = kmalloc(SFS_BUFFER_SIZE*2, GFP_KERNEL); + if (addr == NULL){ + T_MSG("ptr_sfs_buffer : memory not allocated"); + return 0; + } + else { + int offset = ALIGNED_SFS_ADDR(addr); + memset((char *)addr, 0, SFS_BUFFER_SIZE); + fi->q.ptr_sfs_buffers[i*NO_OF_ENTRIES +j] = (u_int *)addr; + addr += offset; + *(fi->q.ptr_sfsbq_base + i*NO_OF_ENTRIES + j) = htonl(virt_to_bus(addr)); + } + } + + /* The number of entries in each SFS buffer is 8. There are 8 + * MFS buffers. That leaves us with 4096-256 bytes. We use them + * as temporary space for ARP frames. This is done inorder to + * support HW_Types of 0x1 and 0x6. + */ + fi->g.arp_buffer = (char *)fi->q.ptr_sfsbq_base + SFSBQ_LENGTH*NO_OF_ENTRIES*4; + + /* build EDB */ + if ((fi->q.ptr_edb_base = (u_int *)__get_free_pages(GFP_KERNEL, 5) ) == 0) { + T_MSG("failed to get EDB page"); + return 0; + } + for (i = 0; i < EDB_LEN; i++) + fi->q.ptr_edb[i] = fi->q.ptr_edb_base + 2*i; + + /* build SEST */ + + /* OX_IDs range from 0x0 - 0x4FFF. + */ + if ((fi->q.ptr_sest_base = (u_int *)__get_free_pages(GFP_KERNEL, 5)) == 0) { + T_MSG("failed to get SEST page"); + return 0; + } + for (i = 0; i < SEST_LENGTH; i++) + fi->q.ptr_sest[i] = fi->q.ptr_sest_base + NO_OF_ENTRIES*i; + + if ((fi->q.ptr_sdb_base = (u_int *)__get_free_pages(GFP_KERNEL, 5)) == 0) { + T_MSG("failed to get SDB page"); + return 0; + } + for (i = 0 ; i < NO_OF_SDB_ENTRIES; i++) + fi->q.ptr_sdb_slot[i] = fi->q.ptr_sdb_base + (SDB_SIZE/4)*i; + + if ((fi->q.ptr_fcp_cmnd_base = (u_int *)__get_free_pages(GFP_KERNEL, 0)) == 0) { + T_MSG("failed to get FCP_CMND page"); + return 0; + } + for (i = 0; i < NO_OF_FCP_CMNDS; i++) + fi->q.ptr_fcp_cmnd[i] = fi->q.ptr_fcp_cmnd_base + NO_OF_ENTRIES*i; + + /* Allocate space for Tachyon Header as well... + */ + if ((fi->q.ptr_tachyon_header_base = (u_int *)__get_free_pages(GFP_KERNEL, 0) ) == 0) { + T_MSG("failed to get tachyon_header page"); + return 0; + } + for (i = 0; i < NO_OF_TACH_HEADERS; i++) + fi->q.ptr_tachyon_header[i] = fi->q.ptr_tachyon_header_base + 16*i; + + /* Allocate memory for indices. + * Indices should be aligned on 32 byte boundries. + */ + fi->q.host_ocq_cons_indx = kmalloc(2*32, GFP_KERNEL); + if (fi->q.host_ocq_cons_indx == NULL){ + T_MSG("fi->q.host_ocq_cons_indx : memory not allocated"); + return 0; + } + fi->q.ptr_host_ocq_cons_indx = fi->q.host_ocq_cons_indx; + if ((u_long)(fi->q.host_ocq_cons_indx) % 32) + fi->q.host_ocq_cons_indx++; + + fi->q.host_hpcq_cons_indx = kmalloc(2*32, GFP_KERNEL); + if (fi->q.host_hpcq_cons_indx == NULL){ + T_MSG("fi->q.host_hpcq_cons_indx : memory not allocated"); + return 0; + } + fi->q.ptr_host_hpcq_cons_indx= fi->q.host_hpcq_cons_indx; + if ((u_long)(fi->q.host_hpcq_cons_indx) % 32) + fi->q.host_hpcq_cons_indx++; + + fi->q.host_imq_prod_indx = kmalloc(2*32, GFP_KERNEL); + if (fi->q.host_imq_prod_indx == NULL){ + T_MSG("fi->q.host_imq_prod_indx : memory not allocated"); + return 0; + } + fi->q.ptr_host_imq_prod_indx = fi->q.host_imq_prod_indx; + if ((u_long)(fi->q.host_imq_prod_indx) % 32) + fi->q.host_imq_prod_indx++; + + LEAVE("build_queues"); + return 1; +} + + +static void write_to_tachyon_registers(struct fc_info *fi) +{ +u_int bus_addr, bus_indx_addr, i; + + ENTER("write_to_tachyon_registers"); + + /* Clear Queues each time Tachyon is reset */ + memset((char *)fi->q.ptr_ocq_base, 0, OCQ_LENGTH * 32); + memset((char *)fi->q.ptr_imq_base, 0, IMQ_LENGTH * 32); + memset((char *)fi->q.ptr_edb_base, 0, EDB_LEN * 8); + memset((char *)fi->q.ptr_sest_base, 0, SEST_LENGTH * 32); + memset((char *)fi->q.ptr_sdb_base, 0, NO_OF_SDB_ENTRIES * SDB_SIZE); + memset((char *)fi->q.ptr_tachyon_header_base, 0xFF, NO_OF_TACH_HEADERS * TACH_HEADER_SIZE); + for (i = 0; i < SEST_LENGTH; i++) + fi->q.free_scsi_oxid[i] = OXID_AVAILABLE; + for (i = 0; i < NO_OF_SDB_ENTRIES; i++) + fi->q.sdb_slot_status[i] = SDB_FREE; + + take_tachyon_offline(fi); + writel(readl(fi->t_r.ptr_tach_config_reg) | SCSI_ENABLE | WRITE_STREAM_SIZE | READ_STREAM_SIZE | PARITY_EVEN | OOO_REASSEMBLY_DISABLE, fi->t_r.ptr_tach_config_reg); + + /* Write OCQ registers */ + fi->q.ocq_prod_indx = 0; + *(fi->q.host_ocq_cons_indx) = 0; + + /* The Tachyon needs to be passed the "real" address */ + bus_addr = virt_to_bus(fi->q.ptr_ocq_base); + writel(bus_addr, fi->t_r.ptr_ocq_base_reg); + writel(OCQ_LENGTH - 1, fi->t_r. ptr_ocq_len_reg); + bus_indx_addr = virt_to_bus(fi->q.host_ocq_cons_indx); + writel(bus_indx_addr, fi->t_r.ptr_ocq_cons_indx_reg); + + /* Write IMQ registers */ + fi->q.imq_cons_indx = 0; + *(fi->q.host_imq_prod_indx) = 0; + bus_addr = virt_to_bus(fi->q.ptr_imq_base); + writel(bus_addr, fi->t_r.ptr_imq_base_reg); + writel(IMQ_LENGTH - 1, fi->t_r.ptr_imq_len_reg); + bus_indx_addr = virt_to_bus(fi->q.host_imq_prod_indx); + writel(bus_indx_addr, fi->t_r.ptr_imq_prod_indx_reg); + + /* Write MFSBQ registers */ + fi->q.mfsbq_prod_indx = MFSBQ_LENGTH - 1; + fi->q.mfsbuff_end = MFS_BUFFER_SIZE - 1; + fi->q.mfsbq_cons_indx = 0; + bus_addr = virt_to_bus(fi->q.ptr_mfsbq_base); + writel(bus_addr, fi->t_r.ptr_mfsbq_base_reg); + writel(MFSBQ_LENGTH - 1, fi->t_r.ptr_mfsbq_len_reg); + writel(fi->q.mfsbuff_end, fi->t_r.ptr_mfsbuff_len_reg); + /* Do this last as tachyon will prefetch the + * first entry as soon as we write to it. + */ + writel(fi->q.mfsbq_prod_indx, fi->t_r.ptr_mfsbq_prod_reg); + + /* Write SFSBQ registers */ + fi->q.sfsbq_prod_indx = SFSBQ_LENGTH - 1; + fi->q.sfsbuff_end = SFS_BUFFER_SIZE - 1; + fi->q.sfsbq_cons_indx = 0; + bus_addr = virt_to_bus(fi->q.ptr_sfsbq_base); + writel(bus_addr, fi->t_r.ptr_sfsbq_base_reg); + writel(SFSBQ_LENGTH - 1, fi->t_r.ptr_sfsbq_len_reg); + writel(fi->q.sfsbuff_end, fi->t_r.ptr_sfsbuff_len_reg); + /* Do this last as tachyon will prefetch the first + * entry as soon as we write to it. + */ + writel(fi->q.sfsbq_prod_indx, fi->t_r.ptr_sfsbq_prod_reg); + + /* Write SEST registers */ + bus_addr = virt_to_bus(fi->q.ptr_sest_base); + writel(bus_addr, fi->t_r.ptr_sest_base_reg); + writel(SEST_LENGTH - 1, fi->t_r.ptr_sest_len_reg); + /* the last 2 bits _should_ be 1 */ + writel(SEST_BUFFER_SIZE - 1, fi->t_r.ptr_scsibuff_len_reg); + + /* write AL_TIME & E_D_TOV into the registers */ + writel(TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + /* Tell Tachyon to pick a Soft Assigned AL_PA */ + writel(LOOP_INIT_SOFT_ADDRESS, fi->t_r.ptr_fm_config_reg); + + /* Read the WWN from EEPROM . But, for now we assign it here. */ + writel(WORLD_WIDE_NAME_LOW, fi->t_r.ptr_fm_wwn_low_reg); + writel(WORLD_WIDE_NAME_HIGH, fi->t_r.ptr_fm_wwn_hi_reg); + + DPRINTK1("TACHYON initializing as L_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + + LEAVE("write_to_tachyon_registers"); +} + + +static void tachyon_interrupt(int irq, void* dev_id, struct pt_regs* regs) +{ +struct Scsi_Host *host = dev_id; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +struct fc_info *fi = hostdata->fi; +u_long flags; + spin_lock_irqsave(&fi->fc_lock, flags); + tachyon_interrupt_handler(irq, dev_id, regs); + spin_unlock_irqrestore(&fi->fc_lock, flags); +} + +static void tachyon_interrupt_handler(int irq, void* dev_id, struct pt_regs* regs) +{ +struct Scsi_Host *host = dev_id; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +struct fc_info *fi = hostdata->fi; +u_int *ptr_imq_entry; +u_int imq_int_type, current_IMQ_index = 0, prev_IMQ_index; +int index, no_of_entries = 0; + + DPRINTK("\n"); + ENTER("tachyon_interrupt"); + if (fi->q.host_imq_prod_indx != NULL) { + current_IMQ_index = ntohl(*(fi->q.host_imq_prod_indx)); + } + else { + /* _Should not_ happen */ + T_MSG("IMQ_indx NULL. DISABLING INTERRUPTS!!!\n"); + writel(0x0, fi->i_r.ptr_ichip_hw_control_reg); + } + + if (current_IMQ_index > fi->q.imq_cons_indx) + no_of_entries = current_IMQ_index - fi->q.imq_cons_indx; + else + if (current_IMQ_index < fi->q.imq_cons_indx) + no_of_entries = IMQ_LENGTH - (fi->q.imq_cons_indx - current_IMQ_index); + + if (no_of_entries == 0) { + u_int ichip_status; + ichip_status = readl(fi->i_r.ptr_ichip_hw_status_reg); + if (ichip_status & 0x20) { + /* Should _never_ happen. Might require a hard reset */ + T_MSG("Too bad... PCI Bus Error. Resetting (i)chip"); + reset_ichip(fi); + T_MSG("DISABLING INTERRUPTS!!!\n"); + writel(0x0, fi->i_r.ptr_ichip_hw_control_reg); + } + } + + prev_IMQ_index = current_IMQ_index; + for (index = 0; index < no_of_entries; index++) { + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + imq_int_type = ntohl(*ptr_imq_entry); + + completion_message_handler(fi, imq_int_type); + if ((fi->g.link_up == FALSE) && ((imq_int_type == MFS_BUF_WARN) || (imq_int_type == SFS_BUF_WARN) || (imq_int_type == IMQ_BUF_WARN))) + break; + update_IMQ_indx(fi, 1); + + /* Check for more entries */ + current_IMQ_index = ntohl(*(fi->q.host_imq_prod_indx)); + if (current_IMQ_index != prev_IMQ_index) { + no_of_entries++; + prev_IMQ_index = current_IMQ_index; + } + } /*end of for loop*/ + return; + LEAVE("tachyon_interrupt"); +} + + +static void handle_SFS_BUF_WARN_interrupt(struct fc_info *fi) +{ +int i; + ENTER("handle_SFS_BUF_WARN_interrupt"); + if (fi->g.link_up == FALSE) { + reset_tachyon(fi, SOFTWARE_RESET); + return; + } + /* Free up all but one entry in the Q. + */ + for (i = 0; i < ((SFSBQ_LENGTH - 1) * NO_OF_ENTRIES); i++) { + handle_SFS_interrupt(fi); + update_IMQ_indx(fi, 1); + } + LEAVE("handle_SFS_BUF_WARN_interrupt"); +} + +/* Untested_Code_Begin */ +static void handle_MFS_BUF_WARN_interrupt(struct fc_info *fi) +{ +int i; + ENTER("handle_MFS_BUF_WARN_interrupt"); + if (fi->g.link_up == FALSE) { + reset_tachyon(fi, SOFTWARE_RESET); + return; + } + /* FIXME: freeing up 8 entries. + */ + for (i = 0; i < NO_OF_ENTRIES; i++) { + handle_MFS_interrupt(fi); + update_IMQ_indx(fi, 1); + } + LEAVE("handle_MFS_BUF_WARN_interrupt"); +} +/*Untested_Code_End */ + +static void handle_IMQ_BUF_WARN_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +u_int imq_int_type, current_IMQ_index = 0, temp_imq_cons_indx; +int index, no_of_entries = 0; + + ENTER("handle_IMQ_BUF_WARN_interrupt"); + if (fi->g.link_up == FALSE) { + reset_tachyon(fi, SOFTWARE_RESET); + return; + } + current_IMQ_index = ntohl(*(fi->q.host_imq_prod_indx)); + + if (current_IMQ_index > fi->q.imq_cons_indx) + no_of_entries = current_IMQ_index - fi->q.imq_cons_indx; + else + if (current_IMQ_index < fi->q.imq_cons_indx) + no_of_entries = IMQ_LENGTH - (fi->q.imq_cons_indx - current_IMQ_index); + /* We dont want to look at the same IMQ entry again. + */ + temp_imq_cons_indx = fi->q.imq_cons_indx + 1; + if (no_of_entries != 0) + no_of_entries -= 1; + for (index = 0; index < no_of_entries; index++) { + ptr_imq_entry = fi->q.ptr_imqe[temp_imq_cons_indx]; + imq_int_type = ntohl(*ptr_imq_entry); + if (imq_int_type != IMQ_BUF_WARN) + completion_message_handler(fi, imq_int_type); + temp_imq_cons_indx++; + if (temp_imq_cons_indx == IMQ_LENGTH) + temp_imq_cons_indx = 0; + } /*end of for loop*/ + if (no_of_entries != 0) + update_IMQ_indx(fi, no_of_entries); + LEAVE("handle_IMQ_BUF_WARN_interrupt"); +} + +static void completion_message_handler(struct fc_info *fi, u_int imq_int_type) +{ + switch(imq_int_type) { + case OUTBOUND_COMPLETION: + DPRINTK("OUTBOUND_COMPLETION message received"); + break; + case OUTBOUND_COMPLETION_I: + DPRINTK("OUTBOUND_COMPLETION_I message received"); + handle_OCI_interrupt(fi); + break; + case OUT_HI_PRI_COMPLETION: + DPRINTK("OUT_HI_PRI_COMPLETION message received"); + break; + case OUT_HI_PRI_COMPLETION_I: + DPRINTK("OUT_HI_PRI_COMPLETION_I message received"); + break; + case INBOUND_MFS_COMPLETION: + DPRINTK("INBOUND_MFS_COMPLETION message received"); + handle_MFS_interrupt(fi); + break; + case INBOUND_OOO_COMPLETION: + DPRINTK("INBOUND_OOO_COMPLETION message received"); + handle_OOO_interrupt(fi); + break; + case INBOUND_SFS_COMPLETION: + DPRINTK("INBOUND_SFS_COMPLETION message received"); + handle_SFS_interrupt(fi); + break; + case INBOUND_UNKNOWN_FRAME_I: + DPRINTK("INBOUND_UNKNOWN_FRAME message received"); + handle_Unknown_Frame_interrupt(fi); + break; + case INBOUND_BUSIED_FRAME: + DPRINTK("INBOUND_BUSIED_FRAME message received"); + handle_Busied_Frame_interrupt(fi); + break; + case FRAME_MGR_INTERRUPT: + DPRINTK("FRAME_MGR_INTERRUPT message received"); + handle_FM_interrupt(fi); + break; + case READ_STATUS: + DPRINTK("READ_STATUS message received"); + break; + case SFS_BUF_WARN: + DPRINTK("SFS_BUF_WARN message received"); + handle_SFS_BUF_WARN_interrupt(fi); + break; + case MFS_BUF_WARN: + DPRINTK("MFS_BUF_WARN message received"); + handle_MFS_BUF_WARN_interrupt(fi); + break; + case IMQ_BUF_WARN: + DPRINTK("IMQ_BUF_WARN message received"); + handle_IMQ_BUF_WARN_interrupt(fi); + break; + case INBOUND_C1_TIMEOUT: + DPRINTK("INBOUND_C1_TIMEOUT message received"); + break; + case BAD_SCSI_FRAME: + DPRINTK("BAD_SCSI_FRAME message received"); + handle_Bad_SCSI_Frame_interrupt(fi); + break; + case INB_SCSI_STATUS_COMPLETION: + DPRINTK("INB_SCSI_STATUS_COMPL message received"); + handle_Inbound_SCSI_Status_interrupt(fi); + break; + case INBOUND_SCSI_COMMAND: + DPRINTK("INBOUND_SCSI_COMMAND message received"); + handle_Inbound_SCSI_Command_interrupt(fi); + break; + case INBOUND_SCSI_DATA_COMPLETION: + DPRINTK("INBOUND_SCSI_DATA message received"); + /* Only for targets */ + break; + default: + T_MSG("DEFAULT message received, type = %x", imq_int_type); + return; + } + reset_latch(fi); +} + +static void handle_OCI_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +u_long transaction_id = 0; +unsigned short status, seq_count, transmitted_ox_id; +struct Scsi_Host *host = fi->host; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +Scsi_Cmnd *Cmnd; +u_int tag; + + ENTER("handle_OCI_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + transaction_id = ntohl(*(ptr_imq_entry + 1)); + status = ntohl(*(ptr_imq_entry + 2)) >> 16; + seq_count = ntohl(*(ptr_imq_entry + 3)); + DPRINTK("transaction_id= %x", (u_int)transaction_id); + tag = transaction_id & 0xFFFF0000; + transmitted_ox_id = transaction_id; + + /* The INT could be either due to TIME_OUT | BAD_ALPA. + * But we check only for TimeOuts. Bad AL_PA will + * caught by FM_interrupt handler. + */ + + if ((status == OCM_TIMEOUT_OR_BAD_ALPA) && (!fi->g.port_discovery) && (!fi->g.perform_adisc)){ + DPRINTK("Frame TimeOut on OX_ID = %x", (u_int)transaction_id); + + /* Is it a SCSI frame that is timing out ? Not a very good check... + */ + if ((transmitted_ox_id <= MAX_SCSI_OXID) && ((tag == FC_SCSI_BAD_TARGET) || (tag < 0x00FF0000))) { + /* If it is a Bad AL_PA, we report it as BAD_TARGET. + * Else, we allow the command to time-out. A Link + * re-initialization could be taking place. + */ + if (tag == FC_SCSI_BAD_TARGET) { + Cmnd = hostdata->cmnd_handler[transmitted_ox_id & MAX_SCSI_XID]; + hostdata->cmnd_handler[transmitted_ox_id & MAX_SCSI_XID] = NULL; + if (Cmnd != NULL) { + Cmnd->result = DID_BAD_TARGET << 16; + (*Cmnd->scsi_done) (Cmnd); + } + else + T_MSG("NULL Command out of handler!"); + } /* if Bad Target */ + else { + u_char missing_target = tag >> 16; + struct fc_node_info *q = fi->node_info_list; + /* A Node that we thought was logged in has gone + * away. We are the optimistic kind and we keep + * hoping that our dear little Target will come back + * to us. For now we log him out. + */ + DPRINTK2("Missing Target = %d", missing_target); + while (q != NULL) { + if (q->target_id == missing_target) { + T_MSG("Target %d Logged out", q->target_id); + q->login = LOGIN_ATTEMPTED; + if (fi->num_nodes > 0) + fi->num_nodes--; + tx_logi(fi, ELS_PLOGI, q->d_id); + break; + } + else + q = q->next; + } + } + } /* End of SCSI frame timing out. */ + else { + if (seq_count > 1) { + /* An IP frame was transmitted to a Bad AL_PA. Free up + * the skb used. + */ + dev_kfree_skb((struct sk_buff *)(bus_to_virt(transaction_id))); + } + } /* End of IP frame timing out. */ + } /* End of frame timing out. */ + else { + /* Frame was transmitted successfully. Check if it was an ELS + * frame or an IP frame or a Bad_Target_Notification frame (in + * case of a ptp_link). Ugly! + */ + if ((status == 0) && (seq_count == 0)) { + u_int tag = transaction_id & 0xFFFF0000; + /* Continue with port discovery after an ELS is successfully + * transmitted. (status == 0). + */ + DPRINTK("tag = %x", tag); + switch(tag) { + case ELS_FLOGI: + /* Letz use the Name Server instead */ + fi->g.explore_fabric = TRUE; + fi->g.port_discovery = FALSE; + fi->g.alpa_list_index = MAX_NODES; + add_to_ox_id_list(fi, transaction_id, tag); + break; + case ELS_PLOGI: + if (fi->g.fabric_present && (fi->g.name_server == FALSE)) + add_to_ox_id_list(fi,transaction_id,ELS_NS_PLOGI); + else + add_to_ox_id_list(fi, transaction_id, tag); + break; + case FC_SCSI_BAD_TARGET: + Cmnd = hostdata->cmnd_handler[transmitted_ox_id & MAX_SCSI_XID]; + hostdata->cmnd_handler[transmitted_ox_id & MAX_SCSI_XID] = NULL; + if (Cmnd != NULL) { + Cmnd->result = DID_BAD_TARGET << 16; + (*Cmnd->scsi_done) (Cmnd); + } + else + T_MSG("NULL Command out of handler!"); + break; + default: + add_to_ox_id_list(fi, transaction_id, tag); + } + + if (fi->g.alpa_list_index >= MAX_NODES) { + if (fi->g.port_discovery == TRUE) { + fi->g.port_discovery = FALSE; + add_display_cache_timer(fi); + } + fi->g.alpa_list_index = MAX_NODES; + } + if (fi->g.port_discovery == TRUE) + local_port_discovery(fi); + } + else { + /* An IP frame has been successfully transmitted. + * Free the skb that was used for this IP frame. + */ + if ((status == 0) && (seq_count > 1)) { + dev_kfree_skb((struct sk_buff *)(bus_to_virt(transaction_id))); + } + } + } + LEAVE("handle_OCI_interrupt"); +} + +/* Right now we discard OOO frames */ +static void handle_OOO_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +int queue_indx, offset, payload_size; +int no_of_buffers = 1; /* header is in a separate buffer */ + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + payload_size = ntohl(*(ptr_imq_entry + 2)) - TACHYON_HEADER_LEN; + /* Calculate total number of buffers */ + no_of_buffers += payload_size / MFS_BUFFER_SIZE; + if (payload_size % MFS_BUFFER_SIZE) + no_of_buffers++; + + /* provide Tachyon will another set of buffers */ + fi->g.mfs_buffer_count += no_of_buffers; + if (fi->g.mfs_buffer_count >= NO_OF_ENTRIES) { + int count = fi->g.mfs_buffer_count / NO_OF_ENTRIES; + fi->g.mfs_buffer_count -= NO_OF_ENTRIES * count; + update_MFSBQ_indx(fi, count); + } +} + +static void handle_MFS_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry, *buff_addr; +u_int type_of_frame, s_id; +int queue_indx, offset, payload_size, starting_indx, starting_offset; +u_short received_ox_id; +int no_of_buffers = 1; /* header is in a separate buffer */ +struct sk_buff *skb; +int wrap_around = FALSE, no_of_wrap_buffs = NO_OF_ENTRIES - 1; + ENTER("handle_MFS_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + DPRINTK("queue_indx = %d, offset = %d\n", queue_indx, offset); + payload_size = ntohl(*(ptr_imq_entry + 2)) - TACHYON_HEADER_LEN; + DPRINTK("payload_size = %d", payload_size); + /* Calculate total number of buffers */ + no_of_buffers += payload_size / MFS_BUFFER_SIZE; + if (payload_size % MFS_BUFFER_SIZE) + no_of_buffers++; + DPRINTK("no_of_buffers = %d", no_of_buffers); + + if ((no_of_buffers - 1) <= offset) { + starting_offset = offset - (no_of_buffers - 1); + starting_indx = queue_indx; + } + else { + int temp = no_of_buffers - (offset + 1); + int no_of_queues = temp / NO_OF_ENTRIES; + starting_offset = temp % NO_OF_ENTRIES; + if (starting_offset != 0) { + no_of_wrap_buffs = starting_offset - 1; //exclude header + starting_offset = NO_OF_ENTRIES - starting_offset; + no_of_queues++; + } + starting_indx = queue_indx - no_of_queues; + if (starting_indx < 0) { + no_of_wrap_buffs -= (starting_indx + 1) * NO_OF_ENTRIES; + starting_indx = MFSBQ_LENGTH + starting_indx; + wrap_around = TRUE; + } + } + + DPRINTK("starting_indx = %d, starting offset = %d no_of_wrap_buffs = %d\n", starting_indx, starting_offset, no_of_wrap_buffs); + /* Get Tachyon Header from first buffer */ + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_mfsbq_base + starting_indx*NO_OF_ENTRIES + starting_offset))); + + + /* extract Type of Frame */ + type_of_frame = (u_int)ntohl(*(buff_addr + 4)) & 0xFF000000; + s_id = (u_int)ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + received_ox_id = ntohl(*(buff_addr + 6)) >> 16; + buff_addr += MFS_BUFFER_SIZE/4; + DPRINTK("type_of_frame = %x, s_id = %x, ox_id = %x", type_of_frame, s_id, received_ox_id); + + switch(type_of_frame) { + case TYPE_LLC_SNAP: + skb = dev_alloc_skb(payload_size); + if (skb == NULL) { + printk(KERN_NOTICE "%s: In handle_MFS_interrupt() Memory squeeze, dropping packet.\n", fi->name); + fi->fc_stats.rx_dropped++; + fi->g.mfs_buffer_count += no_of_buffers; + if (fi->g.mfs_buffer_count >= NO_OF_ENTRIES) { + int count = fi->g.mfs_buffer_count / NO_OF_ENTRIES; + fi->g.mfs_buffer_count -= NO_OF_ENTRIES * count; + update_MFSBQ_indx(fi, count); + return; + } + } + if (wrap_around) { + int wrap_size = no_of_wrap_buffs * MFS_BUFFER_SIZE; + int tail_size = payload_size - wrap_size; + DPRINTK("wrap_size = %d, tail_size = %d\n", wrap_size, tail_size); + if (no_of_wrap_buffs) + memcpy(skb_put(skb, wrap_size), buff_addr, wrap_size); + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_mfsbq_base))); + memcpy(skb_put(skb, tail_size), buff_addr, tail_size); + } + else + memcpy(skb_put(skb, payload_size), buff_addr, payload_size); + rx_net_mfs_packet(fi, skb); + break; + default: + T_MSG("Unknown Frame Type received. Type = %x", type_of_frame); + } + + /* provide Tachyon will another set of buffers */ + fi->g.mfs_buffer_count += no_of_buffers; + if (fi->g.mfs_buffer_count >= NO_OF_ENTRIES) { + int count = fi->g.mfs_buffer_count / NO_OF_ENTRIES; + fi->g.mfs_buffer_count -= NO_OF_ENTRIES * count; + update_MFSBQ_indx(fi, count); + } + LEAVE("handle_MFS_interrupt"); +} + +static void handle_Unknown_Frame_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +int queue_indx, offset; + ENTER("handle_Unknown_Frame_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + /* We discard the "unknown" frame */ + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_Unknown_Frame_interrupt"); +} + +static void handle_Busied_Frame_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +int queue_indx, offset; + ENTER("handle_Busied_Frame_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + /* We discard the "busied" frame */ + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_Busied_Frame_interrupt"); +} + +static void handle_Bad_SCSI_Frame_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry, *buff_addr, *tach_header, *ptr_edb; +u_int s_id, rctl, frame_class, burst_len, transfered_len, len = 0; +int queue_indx, offset, payload_size, i; +u_short ox_id, rx_id, x_id, mtu = 512; +u_char target_id = 0xFF; + + ENTER("handle_Bad_SCSI_Frame_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + payload_size = ntohl(*(ptr_imq_entry + 2)); + + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_sfsbq_base + queue_indx*NO_OF_ENTRIES + offset))); + + rctl = ntohl(*(buff_addr + 2)) & 0xFF000000; + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + ox_id = ntohl(*(buff_addr + 6)) >> 16; + rx_id = ntohl(*(buff_addr + 6)); + x_id = ox_id & MAX_SCSI_XID; + + /* Any frame that comes in with OX_ID that matches an OX_ID + * that has been allocated for SCSI, will be called a Bad + * SCSI frame if the Exchange is not valid any more. + * + * We will also get a Bad SCSI frame interrupt if we receive + * a XFER_RDY with offset != 0. Tachyon washes its hands off + * this Exchange. We have to take care of ourselves. Grrr... + */ + if (rctl == DATA_DESCRIPTOR) { + struct fc_node_info *q = fi->node_info_list; + while (q != NULL) { + if (q->d_id == s_id) { + target_id = q->target_id; + mtu = q->mtu; + break; + } + else + q = q->next; + } + frame_class = target_id; + transfered_len = ntohl(*(buff_addr + 8)); + burst_len = ntohl(*(buff_addr + 9)); + + build_ODB(fi, fi->g.seq_id, s_id, burst_len, 0, mtu, ox_id, rx_id, 0, 0, frame_class << 16); + /* Update the SEQ_ID and Relative Offset in the + * Tachyon Header Structure. + */ + tach_header = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 5))); + *(tach_header + 5) = htonl(fi->g.seq_id << 24); + *(tach_header + 7) = htonl(transfered_len); + fi->g.odb.hdr_addr = *(fi->q.ptr_sest[x_id] + 5); + + /* Invalidate the EDBs used + */ + ptr_edb = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 7))); + + for (i = 0; i < EDB_LEN; i++) + if (fi->q.ptr_edb[i] == ptr_edb) + break; + ptr_edb--; + + if (i < EDB_LEN) { + int j; + do { + ptr_edb += 2; + len += (htonl(*ptr_edb) & 0xFFFF); + j = i; + fi->q.free_edb_list[i++] = EDB_FREE; + if (i == EDB_LEN) { + i = 0; + ptr_edb = fi->q.ptr_edb_base - 1; + } + } while (len < transfered_len); + if (len > transfered_len) { + ptr_edb--; + fi->q.free_edb_list[j] = EDB_BUSY; + } + else + ptr_edb++; + } + else { + T_MSG("EDB not found while freeing"); + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + return; + } + + /* Update the EDB pointer in the ODB. + */ + fi->g.odb.edb_addr = htonl(virt_to_bus(ptr_edb)); + memcpy(fi->q.ptr_odb[fi->q.ocq_prod_indx], &(fi->g.odb), sizeof(ODB)); + /* Update the EDB pointer in the SEST entry. We might need + * this if get another XFER_RDY for the same Exchange. + */ + *(fi->q.ptr_sest[x_id] + 7) = htonl(virt_to_bus(ptr_edb)); + + update_OCQ_indx(fi); + if (fi->g.seq_id == MAX_SEQ_ID) + fi->g.seq_id = 0; + else + fi->g.seq_id++; + } + else + /* Could be a BA_ACC or a BA_RJT. + */ + if (rctl == RCTL_BASIC_ACC) { + u_int bls_type = remove_from_ox_id_list(fi, ox_id); + DPRINTK1("BA_ACC received from S_ID 0x%x with OX_ID = %x in response to %x", s_id, ox_id, bls_type); + if (bls_type == RCTL_BASIC_ABTS) { + u_int STE_bit; + /* Invalidate resources for that Exchange. + */ + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + if (STE_bit & SEST_V) { + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + invalidate_SEST_entry(fi, ox_id); + } + } + } + else + if (rctl == RCTL_BASIC_RJT) { + u_int bls_type = remove_from_ox_id_list(fi, ox_id); + DPRINTK1("BA_RJT received from S_ID 0x%x with OX_ID = %x in response to %x", s_id, ox_id, bls_type); + if (bls_type == RCTL_BASIC_ABTS) { + u_int STE_bit; + /* Invalidate resources for that Exchange. + */ + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + if (STE_bit & SEST_V) { + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + invalidate_SEST_entry(fi, ox_id); + } + } + } + else + DPRINTK1("Frame with R_CTL = %x received from S_ID 0x%x with OX_ID %x", rctl, s_id, ox_id); + + /* Else, discard the "Bad" SCSI frame. + */ + + /* provide Tachyon will another set of buffers + */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_Bad_SCSI_Frame_interrupt"); +} + +static void handle_Inbound_SCSI_Status_interrupt(struct fc_info *fi) +{ +struct Scsi_Host *host = fi->host; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +u_int *ptr_imq_entry, *buff_addr, *ptr_rsp_info, *ptr_sense_info = NULL; +int queue_indx, offset, payload_size; +u_short received_ox_id, x_id; +Scsi_Cmnd *Cmnd; +u_int fcp_status, fcp_rsp_info_len = 0, fcp_sense_info_len = 0, s_id; + ENTER("handle_SCSI_status_interrupt"); + + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_sfsbq_base + queue_indx*NO_OF_ENTRIES + offset))); + payload_size = ntohl(*(ptr_imq_entry + 2)); + received_ox_id = ntohl(*(buff_addr + 6)) >> 16; + + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_sfsbq_base + queue_indx*NO_OF_ENTRIES + offset))); + + fcp_status = ntohl(*(buff_addr + 10)); + ptr_rsp_info = buff_addr + 14; + if (fcp_status & FCP_STATUS_RSP_LEN) + fcp_rsp_info_len = ntohl(*(buff_addr + 13)); + + if (fcp_status & FCP_STATUS_SENSE_LEN) { + ptr_sense_info = ptr_rsp_info + fcp_rsp_info_len / 4; + fcp_sense_info_len = ntohl(*(buff_addr + 12)); + DPRINTK("sense_info = %x", (u_int)ntohl(*ptr_sense_info)); + } + DPRINTK("fcp_status = %x, fcp_rsp_len = %x", fcp_status, fcp_rsp_info_len); + x_id = received_ox_id & MAX_SCSI_XID; + Cmnd = hostdata->cmnd_handler[x_id]; + hostdata->cmnd_handler[x_id] = NULL; + if (Cmnd != NULL) { + memset(Cmnd->sense_buffer, 0, sizeof(Cmnd->sense_buffer)); + /* Check if there is a Sense field */ + if (fcp_status & FCP_STATUS_SENSE_LEN) { + int size = sizeof(Cmnd->sense_buffer); + if (fcp_sense_info_len < size) + size = fcp_sense_info_len; + memcpy(Cmnd->sense_buffer, (char *)ptr_sense_info, size); + } + Cmnd->result = fcp_status & FCP_STATUS_MASK; + (*Cmnd->scsi_done) (Cmnd); + } + else + T_MSG("NULL Command out of handler!"); + + invalidate_SEST_entry(fi, received_ox_id); + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + fi->q.free_scsi_oxid[x_id] = OXID_AVAILABLE; + + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_SCSI_status_interrupt"); +} + +static void invalidate_SEST_entry(struct fc_info *fi, u_short received_ox_id) +{ +u_short x_id = received_ox_id & MAX_SCSI_XID; + /* Invalidate SEST entry if it is an OutBound SEST Entry + */ + if (!(received_ox_id & SCSI_READ_BIT)) { + u_int *ptr_tach_header, *ptr_edb; + u_short temp_ox_id = NOT_SCSI_XID; + int i; + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + + /* Invalidate the Tachyon Header structure + */ + ptr_tach_header = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 5))); + for (i = 0; i < NO_OF_TACH_HEADERS; i++) + if(fi->q.ptr_tachyon_header[i] == ptr_tach_header) + break; + if (i < NO_OF_TACH_HEADERS) + memset(ptr_tach_header, 0xFF, 32); + else + T_MSG("Tachyon Header not found while freeing in invalidate_SEST_entry()"); + + /* Invalidate the EDB used + */ + ptr_edb = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 7))); + for (i = 0; i < EDB_LEN; i++) + if (fi->q.ptr_edb[i] == ptr_edb) + break; + ptr_edb--; + if (i < EDB_LEN) { + do { + ptr_edb += 2; + fi->q.free_edb_list[i++] = EDB_FREE; + if (i == EDB_LEN) { + i = 0; + ptr_edb = fi->q.ptr_edb_base - 1; + } + } while ((htonl(*ptr_edb) & 0x80000000) != 0x80000000); + } + else + T_MSG("EDB not found while freeing in invalidate_SEST_entry()"); + + /* Search for its other header structure and destroy it! + */ + if ((ptr_tach_header + 16) < (fi->q.ptr_tachyon_header_base + (MY_PAGE_SIZE/4))) + ptr_tach_header += 16; + else + ptr_tach_header = fi->q.ptr_tachyon_header_base; + while (temp_ox_id != x_id) { + temp_ox_id = ntohl(*(ptr_tach_header + 6)) >> 16; + if (temp_ox_id == x_id) { + /* Paranoid checking... + */ + for (i = 0; i < NO_OF_TACH_HEADERS; i++) + if(fi->q.ptr_tachyon_header[i] == ptr_tach_header) + break; + if (i < NO_OF_TACH_HEADERS) + memset(ptr_tach_header, 0xFF, 32); + else + T_MSG("Tachyon Header not found while freeing in invalidate_SEST_entry()"); + break; + } + else { + if ((ptr_tach_header + 16) < (fi->q.ptr_tachyon_header_base + (MY_PAGE_SIZE/4))) + ptr_tach_header += 16; + else + ptr_tach_header = fi->q.ptr_tachyon_header_base; + } + } + } + else { + u_short sdb_table_indx; + /* An Inbound Command has completed or needs to be Aborted. + * Clear up the SDB buffers. + */ + sdb_table_indx = *(fi->q.ptr_sest[x_id] + 5); + fi->q.sdb_slot_status[sdb_table_indx] = SDB_FREE; + } +} + +static void handle_Inbound_SCSI_Command_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry; +int queue_indx, offset; + ENTER("handle_Inbound_SCSI_Command_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + /* We discard the SCSI frame as we shouldn't be receiving + * a SCSI Command in the first place + */ + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_Inbound_SCSI_Command_interrupt"); +} + +static void handle_SFS_interrupt(struct fc_info *fi) +{ +u_int *ptr_imq_entry, *buff_addr; +u_int class_of_frame, type_of_frame, s_id, els_type = 0, rctl; +int queue_indx, offset, payload_size, login_state; +u_short received_ox_id, fs_cmnd_code; + ENTER("handle_SFS_interrupt"); + ptr_imq_entry = fi->q.ptr_imqe[fi->q.imq_cons_indx]; + offset = ntohl(*(ptr_imq_entry + 1)) & 0x00000007; + queue_indx = ntohl(*(ptr_imq_entry + 1)) & 0xFFFF0000; + queue_indx = queue_indx >> 16; + DPRINTK("queue_indx = %d, offset = %d\n", queue_indx, offset); + payload_size = ntohl(*(ptr_imq_entry + 2)); + DPRINTK("payload_size = %d", payload_size); + + buff_addr = bus_to_virt(ntohl(*(fi->q.ptr_sfsbq_base + queue_indx*NO_OF_ENTRIES + offset))); + + /* extract Type of Frame */ + type_of_frame = ntohl(*(buff_addr + 4)) & 0xFF000000; + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + received_ox_id = ntohl(*(buff_addr + 6)) >> 16; + switch(type_of_frame) { + case TYPE_BLS: + rctl = ntohl(*(buff_addr + 2)) & 0xFF000000; + switch(rctl) { + case RCTL_BASIC_ABTS: + /* As an Initiator, we should never be receiving + * this. + */ + DPRINTK1("ABTS received from S_ID 0x%x with OX_ID = %x", s_id, received_ox_id); + break; + } + break; + case TYPE_ELS: + class_of_frame = ntohl(*(buff_addr + 8)); + login_state = sid_logged_in(fi, s_id); + switch(class_of_frame & 0xFF000000) { + case ELS_PLOGI: + if (s_id != fi->g.my_id) { + u_int ret_code; + DPRINTK1("PLOGI received from D_ID 0x%x with 0X_ID = %x", s_id, received_ox_id); + if ((ret_code = plogi_ok(fi, buff_addr, payload_size)) == 0){ + tx_logi_acc(fi, ELS_ACC, s_id, received_ox_id); + add_to_address_cache(fi, buff_addr); + } + else { + u_short cmnd_code = ret_code >> 16; + u_short expln_code = ret_code; + tx_ls_rjt(fi, s_id, received_ox_id, cmnd_code, expln_code); + } + } + break; + case ELS_ACC: + els_type = remove_from_ox_id_list(fi, received_ox_id); + DPRINTK1("ELS_ACC received from D_ID 0x%x in response to ELS %x", s_id, els_type); + switch(els_type) { + case ELS_PLOGI: + add_to_address_cache(fi, buff_addr); + tx_prli(fi, ELS_PRLI, s_id, OX_ID_FIRST_SEQUENCE); + break; + case ELS_FLOGI: + add_to_address_cache(fi, buff_addr); + fi->g.my_id = ntohl(*(buff_addr + 2)) & 0x00FFFFFF; + fi->g.fabric_present = TRUE; + fi->g.my_ddaa = fi->g.my_id & 0xFFFF00; + /* Login to the Name Server + */ + tx_logi(fi, ELS_PLOGI, DIRECTORY_SERVER); + break; + case ELS_NS_PLOGI: + fi->g.name_server = TRUE; + add_to_address_cache(fi, buff_addr); + tx_name_server_req(fi, FCS_RFC_4); + tx_scr(fi); + /* Some devices have a delay before + * registering with the Name Server + */ + udelay(500); + tx_name_server_req(fi, FCS_GP_ID4); + break; + case ELS_PRLI: + mark_scsi_sid(fi, buff_addr, ADD_ENTRY); + break; + case ELS_ADISC: + if (!(validate_login(fi, buff_addr))) + tx_logo(fi, s_id, OX_ID_FIRST_SEQUENCE); + break; + } + break; + case ELS_PDISC: + DPRINTK1("ELS_PDISC received from D_ID 0x%x", s_id); + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_ADISC: + DPRINTK1("ELS_ADISC received from D_ID 0x%x", s_id); + if (node_logged_in_prev(fi, buff_addr)) + tx_adisc(fi, ELS_ACC, s_id, received_ox_id); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_PRLI: + DPRINTK1("ELS_PRLI received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) { + tx_prli(fi, ELS_ACC, s_id, received_ox_id); + mark_scsi_sid(fi, buff_addr, ADD_ENTRY); + } + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_PRLO: + DPRINTK1("ELS_PRLO received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_OUT) || (login_state == NODE_NOT_PRESENT)) + tx_logo(fi, s_id, received_ox_id); + else + if (login_state == NODE_LOGGED_IN) + + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + if (login_state == NODE_PROCESS_LOGGED_IN) { + tx_prli(fi, ELS_ACC, s_id, received_ox_id); + mark_scsi_sid(fi, buff_addr, DELETE_ENTRY); + } + break; + case ELS_LS_RJT: + els_type = remove_from_ox_id_list(fi, received_ox_id); + DPRINTK1("ELS_LS_RJT received from D_ID 0x%x in response to %x", s_id, els_type); + /* We should be chking the reason code. + */ + switch (els_type) { + case ELS_ADISC: + tx_logi(fi, ELS_PLOGI, s_id); + break; + } + break; + case ELS_LOGO: + els_type = remove_from_ox_id_list(fi, received_ox_id); + DPRINTK1("ELS_LOGO received from D_ID 0x%x in response to %x", s_id, els_type); + remove_from_address_cache(fi, buff_addr, ELS_LOGO); + tx_acc(fi, s_id, received_ox_id); + if (els_type == ELS_ADISC) + tx_logi(fi, ELS_PLOGI, s_id); + break; + case ELS_RSCN: + DPRINTK1("ELS_RSCN received from D_ID 0x%x", s_id); + tx_acc(fi, s_id, received_ox_id); + remove_from_address_cache(fi, buff_addr, ELS_RSCN); + break; + case ELS_FARP_REQ: + /* We do not support FARP. + So, silently discard it */ + DPRINTK1("ELS_FARP_REQ received from D_ID 0x%x", s_id); + break; + case ELS_ABTX: + DPRINTK1("ELS_ABTX received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_FLOGI: + DPRINTK1("ELS_FLOGI received from D_ID 0x%x", s_id); + if (fi->g.ptp_up == TRUE) { + /* The node could have come up as an N_Port + * in a Loop! So,try initializing as an NL_port + */ + take_tachyon_offline(fi); + /* write AL_TIME & E_D_TOV into the registers */ + writel(TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + writel(LOOP_INIT_SOFT_ADDRESS, fi->t_r.ptr_fm_config_reg); + DPRINTK1("FLOGI received, TACHYON initializing as L_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + else { + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + } + break; + case ELS_ADVC: + DPRINTK1("ELS_ADVC received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_ECHO: + DPRINTK1("ELS_ECHO received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_ESTC: + DPRINTK1("ELS_ESTC received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_ESTS: + DPRINTK1("ELS_ESTS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RCS: + DPRINTK1("ELS_RCS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RES: + DPRINTK1("ELS_RES received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RLS: + DPRINTK1("ELS_RLS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RRQ: + DPRINTK1("ELS_RRQ received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RSS: + DPRINTK1("ELS_RSS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RTV: + DPRINTK1("ELS_RTV received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RSI: + DPRINTK1("ELS_RSI received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_TEST: + /* No reply sequence */ + DPRINTK1("ELS_TEST received from D_ID 0x%x", s_id); + break; + case ELS_RNC: + DPRINTK1("ELS_RNC received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_RVCS: + DPRINTK1("ELS_RVCS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_TPLS: + DPRINTK1("ELS_TPLS received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_GAID: + DPRINTK1("ELS_GAID received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_FACT: + DPRINTK1("ELS_FACT received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_FAN: + /* Hmmm... You don't support FAN ??? */ + DPRINTK1("ELS_FAN received from D_ID 0x%x", s_id); + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + break; + case ELS_FDACT: + DPRINTK1("ELS_FDACT received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_NACT: + DPRINTK1("ELS_NACT received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_NDACT: + DPRINTK1("ELS_NDACT received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_QoSR: + DPRINTK1("ELS_QoSR received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + case ELS_FDISC: + DPRINTK1("ELS_FDISC received from D_ID 0x%x", s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + default: + DPRINTK1("ELS Frame %x received from D_ID 0x%x", class_of_frame, s_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) + tx_ls_rjt(fi, s_id, received_ox_id, CMND_NOT_SUPP, NO_EXPLN); + else + tx_logo(fi, s_id, received_ox_id); + break; + } + break; + case TYPE_FC_SERVICES: + fs_cmnd_code = (ntohl(*(buff_addr + 10)) & 0xFFFF0000) >>16; + switch(fs_cmnd_code) { + case FCS_ACC: + els_type = remove_from_ox_id_list(fi, received_ox_id); + DPRINTK1("FCS_ACC received from D_ID 0x%x in response to %x", s_id, els_type); + if (els_type == FCS_GP_ID4) + explore_fabric(fi, buff_addr); + break; + case FCS_REJECT: + DPRINTK1("FCS_REJECT received from D_ID 0x%x in response to %x", s_id, els_type); + break; + } + break; + case TYPE_LLC_SNAP: + rx_net_packet(fi, (u_char *)buff_addr, payload_size); + break; + default: + T_MSG("Frame Type %x received from %x", type_of_frame, s_id); + } + + /* provide Tachyon will another set of buffers */ + if (offset == (NO_OF_ENTRIES - 1)) + update_SFSBQ_indx(fi); + LEAVE("handle_SFS_interrupt"); +} + +static void handle_FM_interrupt(struct fc_info *fi) +{ +u_int fm_status; +u_int tachyon_status; + + ENTER("handle_FM_interrupt"); + fm_status = readl(fi->t_r.ptr_fm_status_reg); + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + DPRINTK("FM_status = %x, Tachyon_status = %x", fm_status, tachyon_status); + if (fm_status & LINK_DOWN) { + T_MSG("Fibre Channel Link DOWN"); + fm_status = readl(fi->t_r.ptr_fm_status_reg); + + del_timer(&fi->explore_timer); + del_timer(&fi->nport_timer); + del_timer(&fi->lport_timer); + del_timer(&fi->display_cache_timer); + fi->g.link_up = FALSE; + if (fi->g.ptp_up == TRUE) + fi->g.n_port_try = FALSE; + fi->g.ptp_up = FALSE; + fi->g.port_discovery = FALSE; + fi->g.explore_fabric = FALSE; + fi->g.perform_adisc = FALSE; + + /* Logout will all nodes */ + if (fi->node_info_list) { + struct fc_node_info *temp_list = fi->node_info_list; + while(temp_list) { + temp_list->login = LOGIN_ATTEMPTED; + temp_list = temp_list->next; + } + fi->num_nodes = 0; + } + + if ((fi->g.n_port_try == FALSE) && (fi->g.dont_init == FALSE)){ + take_tachyon_offline(fi); + /* write AL_TIME & E_D_TOV into the registers */ + writel(TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + + if ((fi->g.fabric_present == TRUE) && (fi->g.loop_up == TRUE)) { + u_int al_pa = fi->g.my_id & 0xFF; + writel((al_pa << 24) | LOOP_INIT_FABRIC_ADDRESS | LOOP_INIT_PREVIOUS_ADDRESS, fi->t_r.ptr_fm_config_reg); + } + else + if (fi->g.loop_up == TRUE) { + u_int al_pa = fi->g.my_id & 0xFF; + writel((al_pa << 24) | LOOP_INIT_PREVIOUS_ADDRESS, fi->t_r.ptr_fm_config_reg); + } + else + writel(LOOP_INIT_SOFT_ADDRESS, fi->t_r.ptr_fm_config_reg); + fi->g.loop_up = FALSE; + DPRINTK1("In LDWN TACHYON initializing as L_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + } + + if (fm_status & NON_PARTICIPATING) { + T_MSG("Did not acquire an AL_PA. I am not participating"); + } + else + if ((fm_status & LINK_UP) && ((fm_status & LINK_DOWN) == 0)) { + T_MSG("Fibre Channel Link UP"); + if ((fm_status & NON_PARTICIPATING) != TRUE) { + fi->g.link_up = TRUE; + if (tachyon_status & OSM_FROZEN) { + reset_tachyon(fi, ERROR_RELEASE); + reset_tachyon(fi, OCQ_RESET); + } + init_timer(&fi->explore_timer); + init_timer(&fi->nport_timer); + init_timer(&fi->lport_timer); + init_timer(&fi->display_cache_timer); + if ((fm_status & OLD_PORT) == 0) { + fi->g.loop_up = TRUE; + fi->g.ptp_up = FALSE; + fi->g.my_id = readl(fi->t_r.ptr_fm_config_reg) >> 24; + DPRINTK1("My AL_PA = %x", fi->g.my_id); + fi->g.port_discovery = TRUE; + fi->g.explore_fabric = FALSE; + } + else + if (((fm_status & 0xF0) == OLD_PORT) && ((fm_status & 0x0F) == PORT_STATE_ACTIVE)) { + fi->g.loop_up = FALSE; + fi->g.my_id = 0x0; + /* In a point-to-point configuration, we expect to be + * connected to an F_Port. This driver does not yet support + * a configuration where it is connected to another N_Port + * directly. + */ + fi->g.explore_fabric = TRUE; + fi->g.port_discovery = FALSE; + if (fi->g.n_port_try == FALSE) { + take_tachyon_offline(fi); + /* write R_T_TOV & E_D_TOV into the registers */ + writel(PTP_TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + writel(BB_CREDIT | NPORT, fi->t_r.ptr_fm_config_reg); + fi->g.n_port_try = TRUE; + DPRINTK1("In LUP TACHYON initializing as N_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + else { + fi->g.ptp_up = TRUE; + tx_logi(fi, ELS_FLOGI, F_PORT); + } + } + fi->g.my_ddaa = 0x0; + fi->g.fabric_present = FALSE; + /* We havn't sent out any Name Server Reqs */ + fi->g.name_server = FALSE; + fi->g.alpa_list_index = 0; + fi->g.ox_id = NOT_SCSI_XID; + fi->g.my_mtu = FRAME_SIZE; + + /* Implicitly LOGO with all logged-in nodes. + */ + if (fi->node_info_list) { + struct fc_node_info *temp_list = fi->node_info_list; + while(temp_list) { + temp_list->login = LOGIN_ATTEMPTED; + temp_list = temp_list->next; + } + fi->num_nodes = 0; + fi->g.perform_adisc = TRUE; + //fi->g.perform_adisc = FALSE; + fi->g.port_discovery = FALSE; + tx_logi(fi, ELS_FLOGI, F_PORT); + } + else { + /* If Link coming up for the _first_ time or no nodes + * were logged in before... + */ + fi->g.scsi_oxid = 0; + fi->g.seq_id = 0x00; + fi->g.perform_adisc = FALSE; + } + + /* reset OX_ID table */ + while (fi->ox_id_list) { + struct ox_id_els_map *temp = fi->ox_id_list; + fi->ox_id_list = fi->ox_id_list->next; + kfree(temp); + } + fi->ox_id_list = NULL; + } /* End of if partipating */ + } + + if (fm_status & ELASTIC_STORE_ERROR) { + /* Too much junk on the Link + */ + /* Trying to clear it up by Txing PLOGI to urself */ + if (fi->g.link_up == TRUE) + tx_logi(fi, ELS_PLOGI, fi->g.my_id); + } + + if (fm_status & LOOP_UP) { + if (tachyon_status & OSM_FROZEN) { + reset_tachyon(fi, ERROR_RELEASE); + reset_tachyon(fi, OCQ_RESET); + } + } + + if (fm_status & NOS_OLS_RECEIVED){ + if (fi->g.nport_timer_set == FALSE) { + DPRINTK("NOS/OLS Received"); + DPRINTK("FM_status = %x", fm_status); + fi->nport_timer.function = nos_ols_timer; + fi->nport_timer.data = (unsigned long)fi; + fi->nport_timer.expires = RUN_AT((3*HZ)/100); /* 30 msec */ + init_timer(&fi->nport_timer); + add_timer(&fi->nport_timer); + fi->g.nport_timer_set = TRUE; + } + } + + if (((fm_status & 0xF0) == OLD_PORT) && (((fm_status & 0x0F) == PORT_STATE_LF1) || ((fm_status & 0x0F) == PORT_STATE_LF2))) { + DPRINTK1("Link Fail-I in OLD-PORT."); + take_tachyon_offline(fi); + reset_tachyon(fi, SOFTWARE_RESET); + } + + if (fm_status & LOOP_STATE_TIMEOUT){ + if ((fm_status & 0xF0) == ARBITRATING) + DPRINTK1("ED_TOV timesout.In ARBITRATING state..."); + if ((fm_status & 0xF0) == ARB_WON) + DPRINTK1("ED_TOV timesout.In ARBITRATION WON state..."); + if ((fm_status & 0xF0) == OPEN) + DPRINTK1("ED_TOV timesout.In OPEN state..."); + if ((fm_status & 0xF0) == OPENED) + DPRINTK1("ED_TOV timesout.In OPENED state..."); + if ((fm_status & 0xF0) == TX_CLS) + DPRINTK1("ED_TOV timesout.In XMITTED CLOSE state..."); + if ((fm_status & 0xF0) == RX_CLS) + DPRINTK1("ED_TOV timesout.In RECEIVED CLOSE state..."); + if ((fm_status & 0xF0) == INITIALIZING) + DPRINTK1("ED_TOV timesout.In INITIALIZING state..."); + DPRINTK1("Initializing Loop..."); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + + if ((fm_status & BAD_ALPA) && (fi->g.loop_up == TRUE)) { + u_char bad_alpa = (readl(fi->t_r.ptr_fm_rx_al_pa_reg) & 0xFF00) >> 8; + if (tachyon_status & OSM_FROZEN) { + reset_tachyon(fi, ERROR_RELEASE); + reset_tachyon(fi, OCQ_RESET); + } + /* Fix for B34 */ + tx_logi(fi, ELS_PLOGI, fi->g.my_id); + + if (!fi->g.port_discovery && !fi->g.perform_adisc) { + if (bad_alpa != 0xFE) + DPRINTK("Bad AL_PA = %x", bad_alpa); + } + else { + if ((fi->g.perform_adisc == TRUE) && (bad_alpa == 0x00)) { + DPRINTK1("Performing ADISC..."); + fi->g.fabric_present = FALSE; + perform_adisc(fi); + } + } + } + + if (fm_status & LIPF_RECEIVED){ + DPRINTK("LIP(F8) Received"); + } + + if (fm_status & LINK_FAILURE) { + if (fm_status & LOSS_OF_SIGNAL) + DPRINTK1("Detected Loss of Signal."); + if (fm_status & OUT_OF_SYNC) + DPRINTK1("Detected Loss of Synchronization."); + } + + if (fm_status & TRANSMIT_PARITY_ERROR) { + /* Bad! Should not happen. Solution-> Hard Reset. + */ + T_MSG("Parity Error. Perform Hard Reset!"); + } + + if (fi->g.alpa_list_index >= MAX_NODES){ + if (fi->g.port_discovery == TRUE) { + fi->g.port_discovery = FALSE; + add_display_cache_timer(fi); + } + fi->g.alpa_list_index = MAX_NODES; + } + + if (fi->g.port_discovery == TRUE) + local_port_discovery(fi); + + LEAVE("handle_FM_interrupt"); + return; +} + +static void local_port_discovery(struct fc_info *fi) +{ + if (fi->g.loop_up == TRUE) { + /* If this is not here, some of the Bad AL_PAs are missed. + */ + udelay(20); + if ((fi->g.alpa_list_index == 0) && (fi->g.fabric_present == FALSE)){ + tx_logi(fi, ELS_FLOGI, F_PORT); + } + else { + int login_state = sid_logged_in(fi, fi->g.my_ddaa | alpa_list[fi->g.alpa_list_index]); + while ((fi->g.alpa_list_index == 0) || ((fi->g.alpa_list_index < MAX_NODES) && ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN) || (alpa_list[fi->g.alpa_list_index] == (fi->g.my_id & 0xFF))))) + fi->g.alpa_list_index++; + if (fi->g.alpa_list_index < MAX_NODES) + tx_logi(fi, ELS_PLOGI, alpa_list[fi->g.alpa_list_index]); + } + fi->g.alpa_list_index++; + if (fi->g.alpa_list_index >= MAX_NODES){ + if (fi->g.port_discovery == TRUE) { + fi->g.port_discovery = FALSE; + add_display_cache_timer(fi); + } + fi->g.alpa_list_index = MAX_NODES; + } + } +} + +static void nos_ols_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; +u_int fm_status; + fm_status = readl(fi->t_r.ptr_fm_status_reg); + DPRINTK1("FM_status in timer= %x", fm_status); + fi->g.nport_timer_set = FALSE; + del_timer(&fi->nport_timer); + if ((fi->g.ptp_up == TRUE) || (fi->g.loop_up == TRUE)) + return; + if (((fm_status & 0xF0) == OLD_PORT) && (((fm_status & 0x0F) == PORT_STATE_ACTIVE) || ((fm_status & 0x0F) == PORT_STATE_OFFLINE))) { + DPRINTK1("In OLD-PORT after E_D_TOV."); + take_tachyon_offline(fi); + /* write R_T_TOV & E_D_TOV into the registers */ + writel(PTP_TOV_VALUES, fi->t_r.ptr_fm_tov_reg); + writel(BB_CREDIT | NPORT, fi->t_r.ptr_fm_config_reg); + fi->g.n_port_try = TRUE; + DPRINTK1("In timer, TACHYON initializing as N_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } + else + if ((fi->g.lport_timer_set == FALSE) && ((fm_status & 0xF0) == LOOP_FAIL)) { + DPRINTK1("Loop Fail after E_D_TOV."); + fi->lport_timer.function = loop_timer; + fi->lport_timer.data = (unsigned long)fi; + fi->lport_timer.expires = RUN_AT((8*HZ)/100); + init_timer(&fi->lport_timer); + add_timer(&fi->lport_timer); + fi->g.lport_timer_set = TRUE; + take_tachyon_offline(fi); + reset_tachyon(fi, SOFTWARE_RESET); + } + else + if (((fm_status & 0xF0) == OLD_PORT) && (((fm_status & 0x0F) == PORT_STATE_LF1) || ((fm_status & 0x0F) == PORT_STATE_LF2))) { + DPRINTK1("Link Fail-II in OLD-PORT."); + take_tachyon_offline(fi); + reset_tachyon(fi, SOFTWARE_RESET); + } +} + +static void loop_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; + fi->g.lport_timer_set = FALSE; + del_timer(&fi->lport_timer); + if ((fi->g.ptp_up == TRUE) || (fi->g.loop_up == TRUE)) + return; +} + +static void add_display_cache_timer(struct fc_info *fi) +{ + fi->display_cache_timer.function = display_cache_timer; + fi->display_cache_timer.data = (unsigned long)fi; + fi->display_cache_timer.expires = RUN_AT(fi->num_nodes * HZ); + init_timer(&fi->display_cache_timer); + add_timer(&fi->display_cache_timer); +} + +static void display_cache_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; + del_timer(&fi->display_cache_timer); + display_cache(fi); + return; +} + +static void reset_tachyon(struct fc_info *fi, u_int value) +{ +u_int tachyon_status, reset_done = OCQ_RESET_STATUS | SCSI_FREEZE_STATUS; +int not_done = 1, i = 0; + writel(value, fi->t_r.ptr_tach_control_reg); + if (value == OCQ_RESET) + fi->q.ocq_prod_indx = 0; + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + + /* Software resets are immediately done, whereas other aren't. It + about 30 clocks to do the reset */ + if (value != SOFTWARE_RESET) { + while(not_done) { + if (i++ > 100000) { + T_MSG("Reset was unsuccessful! Tachyon Status = %x", tachyon_status); + break; + } + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + if ((tachyon_status & reset_done) == 0) + not_done = 0; + } + } + else { + write_to_tachyon_registers(fi); + } +} + +static void take_tachyon_offline(struct fc_info *fi) +{ +u_int fm_status = readl(fi->t_r.ptr_fm_status_reg); + + /* The first two conditions will never be true. The Manual and + * the errata say this. But the current implementation is + * decently stable. + */ + //if ((fm_status & 0xF0) == LOOP_FAIL) { + if (fm_status == LOOP_FAIL) { + // workaround as in P. 89 + writel(HOST_CONTROL, fi->t_r.ptr_fm_control_reg); + if (fi->g.loop_up == TRUE) + writel(SOFTWARE_RESET, fi->t_r.ptr_tach_control_reg); + else { + writel(OFFLINE, fi->t_r.ptr_fm_control_reg); + writel(EXIT_HOST_CONTROL, fi->t_r.ptr_fm_control_reg); + } + } + else + //if ((fm_status & LOOP_UP) == LOOP_UP) { + if (fm_status == LOOP_UP) { + writel(SOFTWARE_RESET, fi->t_r.ptr_tach_control_reg); + } + else + writel(OFFLINE, fi->t_r.ptr_fm_control_reg); +} + + +static void read_novram(struct fc_info *fi) +{ +int off = 0; + fi->n_r.ptr_novram_hw_control_reg = fi->i_r.ptr_ichip_hw_control_reg; + fi->n_r.ptr_novram_hw_status_reg = fi->i_r.ptr_ichip_hw_status_reg; + iph5526_nr_do_init(fi); + if (fi->clone_id == PCI_VENDOR_ID_INTERPHASE) + off = 32; + + fi->g.my_node_name_high = (fi->n_r.data[off] << 16) | fi->n_r.data[off+1]; + fi->g.my_node_name_low = (fi->n_r.data[off+2] << 16) | fi->n_r.data[off+3]; + fi->g.my_port_name_high = (fi->n_r.data[off+4] << 16) | fi->n_r.data[off+5]; + fi->g.my_port_name_low = (fi->n_r.data[off+6] << 16) | fi->n_r.data[off+7]; + DPRINTK("node_name = %x %x", fi->g.my_node_name_high, fi->g.my_node_name_low); + DPRINTK("port_name = %x %x", fi->g.my_port_name_high, fi->g.my_port_name_low); +} + +static void reset_ichip(struct fc_info *fi) +{ + /* (i)chip reset */ + writel(ICHIP_HCR_RESET, fi->i_r.ptr_ichip_hw_control_reg); + /*wait for chip to get reset */ + udelay(10000); + /*de-assert reset */ + writel(ICHIP_HCR_DERESET, fi->i_r.ptr_ichip_hw_control_reg); + + /* enable INT lines on the (i)chip */ + writel(ICHIP_HCR_ENABLE_INTA , fi->i_r.ptr_ichip_hw_control_reg); + /* enable byte swap */ + writel(ICHIP_HAMR_BYTE_SWAP_ADDR_TR, fi->i_r.ptr_ichip_hw_addr_mask_reg); +} + +static void tx_logi(struct fc_info *fi, u_int logi, u_int d_id) +{ +int int_required = 1; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int r_ctl = RCTL_ELS_UCTL; +u_int type = TYPE_ELS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_logi"); + /* We dont want interrupted for our own logi. + * It screws up the port discovery process. + */ + if (d_id == fi->g.my_id) + int_required = 0; + fill_login_frame(fi, logi); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.login, sizeof(LOGIN)); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),sizeof(LOGIN), r_ctl, type, d_id, my_mtu, int_required, ox_id, logi); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_logi"); + return; +} + +static void tx_logi_acc(struct fc_info *fi, u_int logi, u_int d_id, u_short received_ox_id) +{ +int int_required = 0; +u_int r_ctl = RCTL_ELS_SCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | LAST_SEQUENCE; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_logi_acc"); + fill_login_frame(fi, logi); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.login, sizeof(LOGIN)); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),sizeof(LOGIN), r_ctl, type, d_id, my_mtu, int_required, received_ox_id, logi); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_logi_acc"); + return; +} + +static void tx_prli(struct fc_info *fi, u_int command_code, u_int d_id, u_short received_ox_id) +{ +int int_required = 1; +u_int r_ctl = RCTL_ELS_UCTL; +u_int type = TYPE_ELS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_prli"); + if (command_code == ELS_PRLI) + fi->g.prli.cmnd_code = htons((ELS_PRLI | PAGE_LEN) >> 16); + else { + fi->g.prli.cmnd_code = htons((ELS_ACC | PAGE_LEN) >> 16); + int_required = 0; + type = TYPE_ELS | EXCHANGE_RESPONDER | LAST_SEQUENCE; + r_ctl = RCTL_ELS_SCTL; + } + fi->g.prli.payload_length = htons(PRLI_LEN); + fi->g.prli.type_code = htons(FCP_TYPE_CODE); + fi->g.prli.est_image_pair = htons(IMAGE_PAIR); + fi->g.prli.responder_pa = 0; + fi->g.prli.originator_pa = 0; + fi->g.prli.service_params = htonl(INITIATOR_FUNC | READ_XFER_RDY_DISABLED); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.prli, sizeof(PRLI)); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]), sizeof(PRLI), r_ctl, type, d_id, my_mtu, int_required, received_ox_id, command_code); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_prli"); + return; +} + +static void tx_logo(struct fc_info *fi, u_int d_id, u_short received_ox_id) +{ +int int_required = 1; +u_int r_ctl = RCTL_ELS_UCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | SEQUENCE_RESPONDER | FIRST_SEQUENCE | END_SEQUENCE | SEQUENCE_INITIATIVE; +int size = sizeof(LOGO); +char fc_id[3]; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_logo"); + fi->g.logo.logo_cmnd = htonl(ELS_LOGO); + fi->g.logo.reserved = 0; + memcpy(fc_id, &(fi->g.my_id), 3); + fi->g.logo.n_port_id_0 = fc_id[0]; + fi->g.logo.n_port_id_1 = fc_id[1]; + fi->g.logo.n_port_id_2 = fc_id[2]; + fi->g.logo.port_name_up = htonl(N_PORT_NAME_HIGH); + fi->g.logo.port_name_low = htonl(N_PORT_NAME_LOW); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.logo, sizeof(LOGO)); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, received_ox_id, ELS_LOGO); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_logo"); +} + +static void tx_adisc(struct fc_info *fi, u_int cmnd_code, u_int d_id, u_short received_ox_id) +{ +int int_required = 0; +u_int r_ctl = RCTL_ELS_SCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | SEQUENCE_RESPONDER | FIRST_SEQUENCE | END_SEQUENCE; +int size = sizeof(ADISC); +u_int my_mtu = fi->g.my_mtu; + fi->g.adisc.ls_cmnd_code = htonl(cmnd_code); + fi->g.adisc.hard_address = htonl(0); + fi->g.adisc.port_name_high = htonl(N_PORT_NAME_HIGH); + fi->g.adisc.port_name_low = htonl(N_PORT_NAME_LOW); + fi->g.adisc.node_name_high = htonl(NODE_NAME_HIGH); + fi->g.adisc.node_name_low = htonl(NODE_NAME_LOW); + fi->g.adisc.n_port_id = htonl(fi->g.my_id); + if (cmnd_code == ELS_ADISC) { + int_required = 1; + r_ctl = RCTL_ELS_UCTL; + type = TYPE_ELS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; + } + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.adisc, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, received_ox_id, cmnd_code); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; +} + +static void tx_ls_rjt(struct fc_info *fi, u_int d_id, u_short received_ox_id, u_short reason_code, u_short expln_code) +{ +int int_required = 0; +u_int r_ctl = RCTL_ELS_SCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | LAST_SEQUENCE; +int size = sizeof(LS_RJT); +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_ls_rjt"); + fi->g.ls_rjt.cmnd_code = htonl(ELS_LS_RJT); + fi->g.ls_rjt.reason_code = htonl((reason_code << 16) | expln_code); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.ls_rjt, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, received_ox_id, ELS_LS_RJT); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_ls_rjt"); +} + +static void tx_abts(struct fc_info *fi, u_int d_id, u_short ox_id) +{ +int int_required = 1; +u_int r_ctl = RCTL_BASIC_ABTS; +u_int type = TYPE_BLS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +int size = 0; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_abts"); + fi->g.type_of_frame = FC_BLS; + tx_exchange(fi, NULL, size, r_ctl, type, d_id, my_mtu, int_required, ox_id, RCTL_BASIC_ABTS); + LEAVE("tx_abts"); +} + +static u_int plogi_ok(struct fc_info *fi, u_int *buff_addr, int size) +{ +int ret_code = 0; +u_short mtu = ntohl(*(buff_addr + 10)) & 0x00000FFF; +u_short class3 = ntohl(*(buff_addr + 25)) >> 16; +u_short class3_conc_seq = ntohl(*(buff_addr + 27)) >> 16; +u_short open_seq = ntohl(*(buff_addr + 28)) >> 16; + DPRINTK1("mtu = %x class3 = %x conc_seq = %x open_seq = %x", mtu, class3, class3_conc_seq, open_seq); + size -= TACHYON_HEADER_LEN; + if (!(class3 & 0x8000)) { + DPRINTK1("Received PLOGI with class3 = %x", class3); + ret_code = (LOGICAL_ERR << 16) | NO_EXPLN; + return ret_code; + } + if (mtu < 256) { + DPRINTK1("Received PLOGI with MTU set to %x", mtu); + ret_code = (LOGICAL_ERR << 16) | RECV_FIELD_SIZE; + return ret_code; + } + if (size != PLOGI_LEN) { + DPRINTK1("Received PLOGI of size %x", size); + ret_code = (LOGICAL_ERR << 16) | INV_PAYLOAD_LEN; + return ret_code; + } + if (class3_conc_seq == 0) { + DPRINTK1("Received PLOGI with conc_seq == 0"); + ret_code = (LOGICAL_ERR << 16) | CONC_SEQ; + return ret_code; + } + if (open_seq == 0) { + DPRINTK1("Received PLOGI with open_seq == 0"); + ret_code = (LOGICAL_ERR << 16) | NO_EXPLN; + return ret_code; + } + + /* Could potentially check for more fields, but might end up + not talking to most of the devices. ;-) */ + /* Things that could get checked are: + common_features = 0x8800 + total_concurrent_seq = at least 1 + */ + return ret_code; +} + +static void tx_acc(struct fc_info *fi, u_int d_id, u_short received_ox_id) +{ +int int_required = 0; +u_int r_ctl = RCTL_ELS_SCTL; +u_int type = TYPE_ELS | EXCHANGE_RESPONDER | LAST_SEQUENCE; +int size = sizeof(ACC); +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_acc"); + fi->g.acc.cmnd_code = htonl(ELS_ACC); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.acc, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, received_ox_id, ELS_ACC); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_acc"); +} + + +static void tx_name_server_req(struct fc_info *fi, u_int req) +{ +int int_required = 1, i, size = 0; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int type = TYPE_FC_SERVICES | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_CONTROL; +u_int my_mtu = fi->g.my_mtu, d_id = DIRECTORY_SERVER; +CT_HDR ct_hdr; + ENTER("tx_name_server_req"); + /* Fill up CT_Header */ + ct_hdr.rev_in_id = htonl(FC_CT_REV); + ct_hdr.fs_type = DIRECTORY_SERVER_APP; + ct_hdr.fs_subtype = NAME_SERVICE; + ct_hdr.options = 0; + ct_hdr.resv1 = 0; + ct_hdr.cmnd_resp_code = htons(req >> 16); + ct_hdr.max_res_size = 0; + ct_hdr.resv2 = 0; + ct_hdr.reason_code = 0; + ct_hdr.expln_code = 0; + ct_hdr.vendor_unique = 0; + + fi->g.type_of_frame = FC_ELS; + switch(req) { + case FCS_RFC_4: + memcpy(&(fi->g.rfc_4.ct_hdr), &ct_hdr, sizeof(CT_HDR)); + fi->g.rfc_4.s_id = htonl(fi->g.my_id); + for (i = 0; i < 32; i++) + fi->g.rfc_4.bit_map[i] = 0; + /* We support IP & SCSI */ + fi->g.rfc_4.bit_map[2] = 0x01; + fi->g.rfc_4.bit_map[3] = 0x20; + size = sizeof(RFC_4); + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.rfc_4, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, ox_id, req); + break; + case FCS_GP_ID4: + memcpy(&(fi->g.gp_id4.ct_hdr), &ct_hdr, sizeof(CT_HDR)); + fi->g.gp_id4.port_type = htonl(PORT_TYPE_NX_PORTS); + size = sizeof(GP_ID4); + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.gp_id4, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, ox_id, req); + break; + } + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_name_server_req"); +} + +static void tx_scr(struct fc_info *fi) +{ +int int_required = 1, size = sizeof(SCR); +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int type = TYPE_ELS | SEQUENCE_INITIATIVE | FIRST_SEQUENCE; +u_int r_ctl = RCTL_ELS_UCTL; +u_int my_mtu = fi->g.my_mtu, d_id = FABRIC_CONTROLLER; + ENTER("tx_scr"); + fi->g.scr.cmnd_code = htonl(ELS_SCR); + fi->g.scr.reg_function = htonl(FULL_REGISTRATION); + fi->g.type_of_frame = FC_ELS; + memcpy(fi->g.els_buffer[fi->g.e_i], &fi->g.scr, size); + tx_exchange(fi, (char *)(fi->g.els_buffer[fi->g.e_i]),size, r_ctl, type, d_id, my_mtu, int_required, ox_id, ELS_SCR); + fi->g.e_i++; + if (fi->g.e_i == MAX_PENDING_FRAMES) + fi->g.e_i = 0; + LEAVE("tx_scr"); +} + +static void perform_adisc(struct fc_info *fi) +{ +int count = 0; + /* Will be set to TRUE when timer expires in a PLDA environment. + */ + fi->g.port_discovery = FALSE; + + if (fi->node_info_list) { + struct fc_node_info *temp_list = fi->node_info_list; + while(temp_list) { + /* Tx ADISC to all non-fabric based + * entities. + */ + if ((temp_list->d_id & 0xFF0000) != 0xFF0000) + tx_adisc(fi, ELS_ADISC, temp_list->d_id, OX_ID_FIRST_SEQUENCE); + temp_list = temp_list->next; + udelay(20); + count++; + } + } + /* Perform Port Discovery after timer expires. + * We are giving time for the ADISCed nodes to respond + * so that we dont have to perform PLOGI to those whose + * login are _still_ valid. + */ + fi->explore_timer.function = port_discovery_timer; + fi->explore_timer.data = (unsigned long)fi; + fi->explore_timer.expires = RUN_AT((count*3*HZ)/100); + init_timer(&fi->explore_timer); + add_timer(&fi->explore_timer); +} + +static void explore_fabric(struct fc_info *fi, u_int *buff_addr) +{ +u_int *addr = buff_addr + 12; /* index into payload */ +u_char control_code; +u_int d_id; +int count = 0; + ENTER("explore_fabric"); + DPRINTK1("entering explore_fabric"); + + /*fi->g.perform_adisc = TRUE; + fi->g.explore_fabric = TRUE; + perform_adisc(fi);*/ + + do { + d_id = ntohl(*addr) & 0x00FFFFFF; + if (d_id != fi->g.my_id) { + if (sid_logged_in(fi, d_id) == NODE_NOT_PRESENT) + tx_logi(fi, ELS_PLOGI, d_id); + else + if (sid_logged_in(fi, d_id) == NODE_LOGGED_OUT) + tx_adisc(fi, ELS_ADISC, d_id, OX_ID_FIRST_SEQUENCE); + count++; + } + control_code = (ntohl(*addr) & 0xFF000000) >> 24; + addr++; + DPRINTK1("cc = %x, d_id = %x", control_code, d_id); + } while (control_code != 0x80); + + fi->explore_timer.function = fabric_explore_timer; + fi->explore_timer.data = (unsigned long)fi; + /* We give 30 msec for each device to respond and then send out + * our SCSI enquiries. + */ + fi->explore_timer.expires = RUN_AT((count*3*HZ)/100); + init_timer(&fi->explore_timer); + add_timer(&fi->explore_timer); + + DPRINTK1("leaving explore_fabric"); + LEAVE("explore_fabric"); +} + +static void fabric_explore_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; + del_timer(&fi->explore_timer); + + if ((fi->g.loop_up == TRUE) && (fi->g.ptp_up == FALSE)) { + /* Initiate Local Port Discovery on the Local Loop. + */ + fi->g.port_discovery = TRUE; + fi->g.alpa_list_index = 1; + local_port_discovery(fi); + } + fi->g.explore_fabric = FALSE; + return; +} + +static void port_discovery_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info*)data; + del_timer(&fi->explore_timer); + + if ((fi->g.loop_up == TRUE) && (fi->g.explore_fabric != TRUE)) { + fi->g.port_discovery = TRUE; + fi->g.alpa_list_index = 1; + local_port_discovery(fi); + } + fi->g.perform_adisc = FALSE; + return; +} + +static void add_to_ox_id_list(struct fc_info *fi, u_int transaction_id, u_int cmnd_code) +{ +struct ox_id_els_map *p, *q = fi->ox_id_list, *r = NULL; +int size = sizeof(struct ox_id_els_map); + while (q != NULL) { + r = q; + q = q->next; + } + p = (struct ox_id_els_map *)kmalloc(size, GFP_ATOMIC); + if (p == NULL) { + T_MSG("kmalloc failed in add_to_ox_id_list()"); + return; + } + p->ox_id = transaction_id; + p->els = cmnd_code; + p->next = NULL; + if (fi->ox_id_list == NULL) + fi->ox_id_list = p; + else + r->next = p; + return; +} + +static u_int remove_from_ox_id_list(struct fc_info *fi, u_short received_ox_id) +{ +struct ox_id_els_map *p = fi->ox_id_list, *q = fi->ox_id_list; +u_int els_type; + while (q != NULL) { + if (q->ox_id == received_ox_id) { + + if (q == fi->ox_id_list) + fi->ox_id_list = fi->ox_id_list->next; + else + if (q->next == NULL) + p->next = NULL; + else + p->next = q->next; + + els_type = q->els; + kfree(q); + return els_type; + } + p = q; + q = q->next; + } + if (q == NULL) + DPRINTK2("Could not find ox_id %x in ox_id_els_map", received_ox_id); + return 0; +} + +static void build_tachyon_header(struct fc_info *fi, u_int my_id, u_int r_ctl, u_int d_id, u_int type, u_char seq_id, u_char df_ctl, u_short ox_id, u_short rx_id, char *data) +{ +u_char alpa = d_id & 0x0000FF; +u_int dest_ddaa = d_id &0xFFFF00; + + ENTER("build_tachyon_header"); + DPRINTK("d_id = %x, my_ddaa = %x", d_id, fi->g.my_ddaa); + /* Does it have to go to/thru a Fabric? */ + if ((dest_ddaa != 0) && ((d_id == F_PORT) || (fi->g.fabric_present && (dest_ddaa != fi->g.my_ddaa)))) + alpa = 0x00; + fi->g.tach_header.resv = 0x00000000; + fi->g.tach_header.sof_and_eof = SOFI3 | EOFN; + fi->g.tach_header.dest_alpa = alpa; + /* Set LCr properly to have enuff credit */ + if (alpa == REPLICATE) + fi->g.tach_header.lcr_and_time_stamp = htons(0xC00);/* LCr=3 */ + else + fi->g.tach_header.lcr_and_time_stamp = 0; + fi->g.tach_header.r_ctl_and_d_id = htonl(r_ctl | d_id); + fi->g.tach_header.vc_id_and_s_id = htonl(my_id); + fi->g.tach_header.type_and_f_cntl = htonl(type); + fi->g.tach_header.seq_id = seq_id; + fi->g.tach_header.df_cntl = df_ctl; + fi->g.tach_header.seq_cnt = 0; + fi->g.tach_header.ox_id = htons(ox_id); + fi->g.tach_header.rx_id = htons(rx_id); + fi->g.tach_header.ro = 0; + if (data) { + /* We use the Seq_Count to keep track of IP frames in the + * OCI_interrupt handler. Initial Seq_Count of IP frames is 1. + */ + if (fi->g.type_of_frame == FC_BROADCAST) + fi->g.tach_header.seq_cnt = htons(0x1); + else + fi->g.tach_header.seq_cnt = htons(0x2); + fi->g.tach_header.nw_header.d_naa = htons(0x1000); + fi->g.tach_header.nw_header.s_naa = htons(0x1000); + memcpy(&(fi->g.tach_header.nw_header.dest_high), data, 2); + memcpy(&(fi->g.tach_header.nw_header.dest_low), data + 2, 4); + memcpy(&(fi->g.tach_header.nw_header.source_high), data + 6, 2); + memcpy(&(fi->g.tach_header.nw_header.source_low), data + 8, 4); + } + LEAVE("build_tachyon_header"); +} + +static void build_EDB(struct fc_info *fi, char *data, u_short flags, u_short len) +{ + fi->g.edb.buf_addr = ntohl((u_int)virt_to_bus(data)); + fi->g.edb.ehf = ntohs(flags); + if (len % 4) + len += (4 - (len % 4)); + fi->g.edb.buf_len = ntohs(len); +} + +static void build_ODB(struct fc_info *fi, u_char seq_id, u_int d_id, u_int len, u_int cntl, u_short mtu, u_short ox_id, u_short rx_id, int NW_header, int int_required, u_int frame_class) +{ + fi->g.odb.seq_d_id = htonl(seq_id << 24 | d_id); + fi->g.odb.tot_len = len; + if (NW_header) + fi->g.odb.tot_len += NW_HEADER_LEN; + if (fi->g.odb.tot_len % 4) + fi->g.odb.tot_len += (4 - (fi->g.odb.tot_len % 4)); + fi->g.odb.tot_len = htonl(fi->g.odb.tot_len); + switch(int_required) { + case NO_COMP_AND_INT: + fi->g.odb.cntl = htons(ODB_CLASS_3 | ODB_EE_CREDIT | ODB_NO_INT | ODB_NO_COMP | cntl); + break; + case INT_AND_COMP_REQ: + fi->g.odb.cntl = htons(ODB_CLASS_3 | ODB_EE_CREDIT | cntl); + break; + case NO_INT_COMP_REQ: + fi->g.odb.cntl = htons(ODB_CLASS_3 | ODB_EE_CREDIT | ODB_NO_INT | cntl); + break; + } + fi->g.odb.rx_id = htons(rx_id); + fi->g.odb.cs_enable = 0; + fi->g.odb.cs_seed = htons(1); + + fi->g.odb.hdr_addr = htonl(virt_to_bus(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx])); + fi->g.odb.frame_len = htons(mtu); + + if (NW_header) { + /* The pointer to the sk_buff is in here. Freed up when the + * OCI_interrupt is received. + */ + fi->g.odb.trans_id = htonl(frame_class); + fi->g.odb.hdr_len = TACHYON_HEADER_LEN + NW_HEADER_LEN; + } + else { + /* helps in tracking transmitted OX_IDs */ + fi->g.odb.trans_id = htonl((frame_class & 0xFFFF0000) | ox_id); + fi->g.odb.hdr_len = TACHYON_HEADER_LEN; + } + fi->g.odb.hdr_len = htons(fi->g.odb.hdr_len); + + fi->g.odb.edb_addr = htonl(virt_to_bus(fi->q.ptr_edb[fi->q.edb_buffer_indx])); +} + +static void fill_login_frame(struct fc_info *fi, u_int logi) +{ +int i; + fi->g.login.ls_cmnd_code= htonl(logi); + fi->g.login.fc_ph_version = htons(PH_VERSION); + if (fi->g.loop_up) + fi->g.login.buff_to_buff_credit = htons(LOOP_BB_CREDIT); + else + if (fi->g.ptp_up) + fi->g.login.buff_to_buff_credit = htons(PT2PT_BB_CREDIT); + if ((logi != ELS_FLOGI) || (logi == ELS_ACC)) + fi->g.login.common_features = htons(PLOGI_C_F); + else + if (logi == ELS_FLOGI) + fi->g.login.common_features = htons(FLOGI_C_F); + fi->g.login.recv_data_field_size = htons(FRAME_SIZE); + fi->g.login.n_port_total_conc_seq = htons(CONCURRENT_SEQUENCES); + fi->g.login.rel_off_by_info_cat = htons(RO_INFO_CATEGORY); + fi->g.login.ED_TOV = htonl(E_D_TOV); + fi->g.login.n_port_name_high = htonl(N_PORT_NAME_HIGH); + fi->g.login.n_port_name_low = htonl(N_PORT_NAME_LOW); + fi->g.login.node_name_high = htonl(NODE_NAME_HIGH); + fi->g.login.node_name_low = htonl(NODE_NAME_LOW); + + /* Fill Class 1 parameters */ + fi->g.login.c_of_s[0].service_options = htons(0); + fi->g.login.c_of_s[0].initiator_ctl = htons(0); + fi->g.login.c_of_s[0].recipient_ctl = htons(0); + fi->g.login.c_of_s[0].recv_data_field_size = htons(0); + fi->g.login.c_of_s[0].concurrent_sequences = htons(0); + fi->g.login.c_of_s[0].n_port_end_to_end_credit = htons(0); + fi->g.login.c_of_s[0].open_seq_per_exchange = htons(0); + fi->g.login.c_of_s[0].resv = htons(0); + + /* Fill Class 2 parameters */ + fi->g.login.c_of_s[1].service_options = htons(0); + fi->g.login.c_of_s[1].initiator_ctl = htons(0); + fi->g.login.c_of_s[1].recipient_ctl = htons(0); + fi->g.login.c_of_s[1].recv_data_field_size = htons(0); + fi->g.login.c_of_s[1].concurrent_sequences = htons(0); + fi->g.login.c_of_s[1].n_port_end_to_end_credit = htons(0); + fi->g.login.c_of_s[1].open_seq_per_exchange = htons(0); + fi->g.login.c_of_s[1].resv = htons(0); + + /* Fill Class 3 parameters */ + if (logi == ELS_FLOGI) + fi->g.login.c_of_s[2].service_options = htons(SERVICE_VALID | SEQUENCE_DELIVERY); + else + fi->g.login.c_of_s[2].service_options = htons(SERVICE_VALID); + fi->g.login.c_of_s[2].initiator_ctl = htons(0); + fi->g.login.c_of_s[2].recipient_ctl = htons(0); + fi->g.login.c_of_s[2].recv_data_field_size = htons(FRAME_SIZE); + fi->g.login.c_of_s[2].concurrent_sequences = htons(CLASS3_CONCURRENT_SEQUENCE); + fi->g.login.c_of_s[2].n_port_end_to_end_credit = htons(0); + fi->g.login.c_of_s[2].open_seq_per_exchange = htons(CLASS3_OPEN_SEQUENCE); + fi->g.login.c_of_s[2].resv = htons(0); + + for(i = 0; i < 4; i++) { + fi->g.login.resv[i] = 0; + fi->g.login.vendor_version_level[i] = 0; + } +} + + +/* clear the Interrupt Latch on the (i)chip, so that you can receive + * Interrupts from Tachyon in future + */ +static void reset_latch(struct fc_info *fi) +{ + writel(readl(fi->i_r.ptr_ichip_hw_status_reg) | ICHIP_HSR_INT_LATCH, fi->i_r.ptr_ichip_hw_status_reg); +} + +static void update_OCQ_indx(struct fc_info *fi) +{ + fi->q.ocq_prod_indx++; + if (fi->q.ocq_prod_indx == OCQ_LENGTH) + fi->q.ocq_prod_indx = 0; + writel(fi->q.ocq_prod_indx, fi->t_r.ptr_ocq_prod_indx_reg); +} + +static void update_IMQ_indx(struct fc_info *fi, int count) +{ + fi->q.imq_cons_indx += count; + if (fi->q.imq_cons_indx >= IMQ_LENGTH) + fi->q.imq_cons_indx -= IMQ_LENGTH; + writel(fi->q.imq_cons_indx, fi->t_r.ptr_imq_cons_indx_reg); +} + +static void update_SFSBQ_indx(struct fc_info *fi) +{ + fi->q.sfsbq_prod_indx++; + if (fi->q.sfsbq_prod_indx == SFSBQ_LENGTH) + fi->q.sfsbq_prod_indx = 0; + writel(fi->q.sfsbq_prod_indx, fi->t_r.ptr_sfsbq_prod_reg); +} + +static void update_MFSBQ_indx(struct fc_info *fi, int count) +{ + fi->q.mfsbq_prod_indx += count; + if (fi->q.mfsbq_prod_indx >= MFSBQ_LENGTH) + fi->q.mfsbq_prod_indx -= MFSBQ_LENGTH; + writel(fi->q.mfsbq_prod_indx, fi->t_r.ptr_mfsbq_prod_reg); +} + + +static void update_tachyon_header_indx(struct fc_info *fi) +{ + fi->q.tachyon_header_indx++; + if (fi->q.tachyon_header_indx == NO_OF_TACH_HEADERS) + fi->q.tachyon_header_indx = 0; +} + +static void update_EDB_indx(struct fc_info *fi) +{ + fi->q.edb_buffer_indx++; + if (fi->q.edb_buffer_indx == EDB_LEN) + fi->q.edb_buffer_indx = 0; +} + +static int iph5526_open(struct device *dev) +{ + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + MOD_INC_USE_COUNT; + return 0; +} + +static int iph5526_close(struct device *dev) +{ + dev->tbusy = 1; + dev->start = 0; + MOD_DEC_USE_COUNT; + return 0; +} + +static int iph5526_send_packet(struct sk_buff *skb, struct device *dev) +{ +struct fc_info *fi = (struct fc_info*)dev->priv; +int status = 0; +short type = 0; +u_long flags; + ENTER("iph5526_send_packet"); + if (dev->tbusy) { + printk(KERN_WARNING "%s: DEVICE BUSY\n", dev->name); + dev->tbusy = 0; + fi->fc_stats.rx_dropped++; + dev->trans_start = jiffies; + return 0; + } + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk(KERN_WARNING "%s: Transmitter access conflict.\n", +dev->name); + fi->fc_stats.rx_dropped++; + return 1; + } + else { + struct fcllc *fcllc; + /* Strip off the pseudo header. + */ + skb->data = skb->data + 2*FC_ALEN; + skb->len = skb->len - 2*FC_ALEN; + fcllc = (struct fcllc *)skb->data; + type = ntohs(fcllc->ethertype); + + spin_lock_irqsave(&fi->fc_lock, flags); + switch(type) { + case ETH_P_IP: + status = tx_ip_packet(skb, skb->len, fi); + break; + case ETH_P_ARP: + status = tx_arp_packet(skb->data, skb->len, fi); + break; + default: + T_MSG("WARNING!!! Received Unknown Packet Type... Discarding..."); + fi->fc_stats.rx_dropped++; + break; + } + spin_unlock_irqrestore(&fi->fc_lock, flags); + } + + if (status) { + fi->fc_stats.tx_bytes += skb->len; + fi->fc_stats.tx_packets++; + } + else + fi->fc_stats.rx_dropped++; + dev->trans_start = jiffies; + dev->tbusy = 0; + /* We free up the IP buffers in the OCI_interrupt handler. + * status == 0 implies that the frame was not transmitted. So the + * skb is freed here. + */ + if ((type == ETH_P_ARP) || (status == 0)) + dev_kfree_skb(skb); + mark_bh(NET_BH); + LEAVE("iph5526_send_packet"); + return 0; +} + +static int iph5526_change_mtu(struct device *dev, int mtu) +{ + return 0; +} + +static int tx_ip_packet(struct sk_buff *skb, unsigned long len, struct fc_info *fi) +{ +u_int d_id; +int int_required = 1; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_DATA; +u_int type = TYPE_LLC_SNAP; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int mtu; +struct fc_node_info *q; + + ENTER("tx_ip_packet"); + q = look_up_cache(fi, skb->data - 2*FC_ALEN); + if (q != NULL) { + d_id = q->d_id; + DPRINTK("Look-Up Cache Succeeded for d_id = %x", d_id); + mtu = q->mtu; + if (q->login == LOGIN_COMPLETED){ + fi->g.type_of_frame = FC_IP; + return tx_exchange(fi, skb->data, len, r_ctl, type, d_id, mtu, int_required, ox_id, virt_to_bus(skb)); + } + + if (q->d_id == BROADCAST) { + struct fc_node_info *p = fi->node_info_list; + int return_value = FALSE; + fi->g.type_of_frame = FC_BROADCAST; + /* Do unicast to local nodes. + */ + int_required = 0; + while(p != NULL) { + d_id = p->d_id; + if ((d_id & 0xFFFF00) == fi->g.my_ddaa) + return_value |= tx_exchange(fi, skb->data, len, r_ctl, type, d_id, fi->g.my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + p = p->next; + } + kfree(q); + return return_value; + } + + if (q->login != LOGIN_COMPLETED) { + DPRINTK1("Node not logged in... Txing PLOGI to %x", d_id); + /* FIXME: we are dumping the frame here */ + tx_logi(fi, ELS_PLOGI, d_id); + } + } + DPRINTK2("Look-Up Cache Failed"); + LEAVE("tx_ip_packet"); + return 0; +} + +static int tx_arp_packet(char *data, unsigned long len, struct fc_info *fi) +{ +u_int opcode = data[ARP_OPCODE_0]; +u_int d_id; +int int_required = 0, return_value = FALSE; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_DATA; +u_int type = TYPE_LLC_SNAP; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +u_int my_mtu = fi->g.my_mtu; + ENTER("tx_arp_packet"); + + opcode = opcode << 8 | data[ARP_OPCODE_1]; + fi->g.type_of_frame = FC_IP; + + if (opcode == ARPOP_REQUEST) { + struct fc_node_info *q = fi->node_info_list; + d_id = BROADCAST; + return_value |= tx_exchange(fi, data, len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + /* Some devices support HW_TYPE 0x01 */ + memcpy(fi->g.arp_buffer, data - 2*FC_ALEN, len + 2*FC_ALEN); + fi->g.arp_buffer[9 + 2*FC_ALEN] = 0x01; + return_value |= tx_exchange(fi, (char *)(fi->g.arp_buffer + 2*FC_ALEN), len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + + /* Do unicast to local nodes. + */ + while(q != NULL) { + fi->g.type_of_frame = FC_BROADCAST; + d_id = q->d_id; + if ((d_id & 0xFFFF00) == fi->g.my_ddaa) { + return_value |= tx_exchange(fi, data, len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + // Some devices support HW_TYPE 0x01 + memcpy(fi->g.arp_buffer, data - 2*FC_ALEN, len + 2*FC_ALEN); + fi->g.arp_buffer[9 + 2*FC_ALEN] = 0x01; + return_value |= tx_exchange(fi, (char *)(fi->g.arp_buffer + 2*FC_ALEN), len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + } + q = q->next; + } + return return_value; + } + else + if (opcode == ARPOP_REPLY) { + struct fc_node_info *q; u_int mtu; + DPRINTK("We are sending out an ARP reply"); + q = look_up_cache(fi, data - 2*FC_ALEN); + if (q != NULL) { + d_id = q->d_id; + DPRINTK("Look-Up Cache Succeeded for d_id = %x", d_id); + mtu = q->mtu; + if (q->login == LOGIN_COMPLETED){ + tx_exchange(fi, data, len, r_ctl, type, d_id, mtu, int_required, ox_id, TYPE_LLC_SNAP); + /* Some devices support HW_TYPE 0x01 */ + memcpy(fi->g.arp_buffer, data - 2*FC_ALEN, len + 2*FC_ALEN); + fi->g.arp_buffer[9 + 2*FC_ALEN] = 0x01; + return tx_exchange(fi, (char *)(fi->g.arp_buffer + 2*FC_ALEN), len, r_ctl, type, d_id, my_mtu, int_required, ox_id, TYPE_LLC_SNAP); + } + else { + DPRINTK1("Node not logged in... Txing PLOGI to %x", d_id); + tx_logi(fi, ELS_PLOGI, d_id); /* FIXME: we are dumping the frame here */ + } + } + DPRINTK2("Look-Up Cache Failed"); + } + else { + T_MSG("Warning!!! Invalid Opcode in ARP Packet!"); + } + LEAVE("tx_arp_packet"); + return 0; +} + + +static void rx_net_packet(struct fc_info *fi, u_char *buff_addr, int payload_size) +{ +struct device *dev = fi->dev; +struct sk_buff *skb; +u_int skb_size = 0; +struct fch_hdr fch; + ENTER("rx_net_packet"); + skb_size = payload_size - TACHYON_HEADER_LEN; + DPRINTK("skb_size = %d", skb_size); + fi->fc_stats.rx_bytes += skb_size - 2; + skb = dev_alloc_skb(skb_size); + if (skb == NULL) { + printk(KERN_NOTICE "%s: In rx_net_packet() Memory squeeze, dropping packet.\n", dev->name); + fi->fc_stats.rx_dropped++; + return; + } + /* Skip over the Tachyon Frame Header. + */ + buff_addr += TACHYON_HEADER_LEN; + + memcpy(fch.daddr, buff_addr + 2, FC_ALEN); + memcpy(fch.saddr, buff_addr + 10, FC_ALEN); + buff_addr += 2; + memcpy(buff_addr, fch.daddr, FC_ALEN); + memcpy(buff_addr + 6, fch.saddr, FC_ALEN); + skb_reserve(skb, 2); + memcpy(skb_put(skb, skb_size - 2), buff_addr, skb_size - 2); + skb->dev = dev; + skb->protocol = fc_type_trans(skb, dev); + DPRINTK("protocol = %x", skb->protocol); + + /* Hmmm... to accept HW Type 0x01 as well... + */ + if (skb->protocol == ntohs(ETH_P_ARP)) + skb->data[1] = 0x06; + netif_rx(skb); + fi->fc_stats.rx_packets++; + LEAVE("rx_net_packet"); +} + + +static void rx_net_mfs_packet(struct fc_info *fi, struct sk_buff *skb) +{ +struct device *dev = fi->dev; +struct fch_hdr fch; + ENTER("rx_net_mfs_packet"); + /* Construct your Hard Header */ + memcpy(fch.daddr, skb->data + 2, FC_ALEN); + memcpy(fch.saddr, skb->data + 10, FC_ALEN); + skb_pull(skb, 2); + memcpy(skb->data, fch.daddr, FC_ALEN); + memcpy(skb->data + 6, fch.saddr, FC_ALEN); + skb->dev = dev; + skb->protocol = fc_type_trans(skb, dev); + DPRINTK("protocol = %x", skb->protocol); + netif_rx(skb); + LEAVE("rx_net_mfs_packet"); +} + +unsigned short fc_type_trans(struct sk_buff *skb, struct device *dev) +{ +struct fch_hdr *fch=(struct fch_hdr *)skb->data; +struct fcllc *fcllc; + skb->mac.raw = skb->data; + fcllc = (struct fcllc *)(skb->data + sizeof(struct fch_hdr) + 2); + skb_pull(skb,sizeof(struct fch_hdr) + 2); + + if(*fch->daddr & 1) { + if(!memcmp(fch->daddr,dev->broadcast,FC_ALEN)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } + else if(dev->flags & IFF_PROMISC) { + if(memcmp(fch->daddr, dev->dev_addr, FC_ALEN)) + skb->pkt_type=PACKET_OTHERHOST; + } + + /* Strip the SNAP header from ARP packets since we don't + * pass them through to the 802.2/SNAP layers. + */ + + if (fcllc->dsap == EXTENDED_SAP && + (fcllc->ethertype == ntohs(ETH_P_IP) || + fcllc->ethertype == ntohs(ETH_P_ARP))) { + skb_pull(skb, sizeof(struct fcllc)); + return fcllc->ethertype; + } + return ntohs(ETH_P_802_2); +} + +static int tx_exchange(struct fc_info *fi, char *data, u_int len, u_int r_ctl, u_int type, u_int d_id, u_int mtu, int int_required, u_short tx_ox_id, u_int frame_class) +{ +u_char df_ctl; +int NW_flag = 0, h_size, return_value; +u_short rx_id = RX_ID_FIRST_SEQUENCE; +u_int tachyon_status; +u_int my_id = fi->g.my_id; + ENTER("tx_exchange"); + + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + DPRINTK("Tachyon Status = %x len = %d MTU = %d", tachyon_status, len, mtu); + if (tachyon_status & OSM_FROZEN) { + reset_tachyon(fi, ERROR_RELEASE); + reset_tachyon(fi, OCQ_RESET); + DPRINTK("Tachyon Status = %x len = %d MTU = %d", tachyon_status, len, mtu); + } + if (tx_ox_id == OX_ID_FIRST_SEQUENCE) { + switch(fi->g.type_of_frame) { + case FC_SCSI_READ: + tx_ox_id = fi->g.scsi_oxid | SCSI_READ_BIT; + break; + case FC_SCSI_WRITE: + tx_ox_id = fi->g.scsi_oxid; + break; + default: + tx_ox_id = fi->g.ox_id; + break; + } + } + else { + switch(fi->g.type_of_frame) { + case FC_SCSI_READ: + rx_id = fi->g.scsi_oxid | SCSI_READ_BIT; + break; + case FC_SCSI_WRITE: + rx_id = fi->g.scsi_oxid; + break; + case FC_BLS: + rx_id = RX_ID_FIRST_SEQUENCE; + break; + default: + rx_id = fi->g.ox_id; + break; + } + } + + if (type == TYPE_LLC_SNAP) { + df_ctl = 0x20; + NW_flag = 1; + /* Multi Frame Sequence ? If yes, set RO bit */ + if (len > mtu) + type |= RELATIVE_OFF_PRESENT; + build_tachyon_header(fi, my_id, r_ctl, d_id, type, fi->g.seq_id, df_ctl, tx_ox_id, rx_id, data - 2*FC_ALEN); + } + else { + df_ctl = 0; + /* Multi Frame Sequence ? If yes, set RO bit */ + if (len > mtu) + type |= RELATIVE_OFF_PRESENT; + build_tachyon_header(fi, my_id, r_ctl, d_id, type, fi->g.seq_id, df_ctl, tx_ox_id, rx_id, NULL); + } + + /* Get free Tachyon Headers and EDBs */ + if (get_free_header(fi) || get_free_EDB(fi)) + return 0; + + if ((type & 0xFF000000) == TYPE_LLC_SNAP) { + h_size = TACHYON_HEADER_LEN + NW_HEADER_LEN; + memcpy(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx], &(fi->g.tach_header), h_size); + } + else + memcpy(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx], &(fi->g.tach_header), TACHYON_HEADER_LEN); + + return_value = tx_sequence(fi, data, len, mtu, d_id, tx_ox_id, rx_id, fi->g.seq_id, NW_flag, int_required, frame_class); + + switch(fi->g.type_of_frame) { + case FC_SCSI_READ: + case FC_SCSI_WRITE: + update_scsi_oxid(fi); + break; + case FC_BLS: + break; + default: + fi->g.ox_id++; + if (fi->g.ox_id == 0xFFFF) + fi->g.ox_id = NOT_SCSI_XID; + break; + } + + if (fi->g.seq_id == MAX_SEQ_ID) + fi->g.seq_id = 0; + else + fi->g.seq_id++; + LEAVE("tx_exchange"); + return return_value; +} + +static int tx_sequence(struct fc_info *fi, char *data, u_int len, u_int mtu, u_int d_id, u_short ox_id, u_short rx_id, u_char seq_id, int NW_flag, int int_required, u_int frame_class) +{ +u_int cntl = 0; +int return_value; + ENTER("tx_sequence"); + build_EDB(fi, data, EDB_END, len); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + build_ODB(fi, seq_id, d_id, len, cntl, mtu, ox_id, rx_id, NW_flag, int_required, frame_class); + memcpy(fi->q.ptr_odb[fi->q.ocq_prod_indx], &(fi->g.odb), sizeof(ODB)); + if (fi->g.link_up != TRUE) { + DPRINTK2("Fibre Channel Link not up. Dropping Exchange!"); + return_value = FALSE; + } + else { + /* To be on the safe side, a check should be included + * at this point to check if we are overrunning + * Tachyon. + */ + update_OCQ_indx(fi); + return_value = TRUE; + } + update_EDB_indx(fi); + update_tachyon_header_indx(fi); + LEAVE("tx_sequence"); + return return_value; +} + +static int get_free_header(struct fc_info *fi) +{ +u_short temp_ox_id; +u_int *tach_header, initial_indx = fi->q.tachyon_header_indx; + /* Check if the header is in use. + * We could have an outstanding command. + * We should find a free slot as we can queue a + * maximum of 32 SCSI commands only. + */ + tach_header = fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx]; + temp_ox_id = ntohl(*(tach_header + 6)) >> 16; + /* We care about the SCSI writes only. Those are the wicked ones + * that need an additional set of buffers. + */ + while(temp_ox_id <= MAX_SCSI_XID) { + update_tachyon_header_indx(fi); + if (fi->q.tachyon_header_indx == initial_indx) { + /* Should never happen. + */ + T_MSG("No free Tachyon headers available"); + reset_tachyon(fi, SOFTWARE_RESET); + return 1; + } + tach_header = fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx]; + temp_ox_id = ntohl(*(tach_header + 6)) >> 16; + } + return 0; +} + +static int get_free_EDB(struct fc_info *fi) +{ +unsigned int initial_indx = fi->q.edb_buffer_indx; + /* Check if the EDB is in use. + * We could have an outstanding SCSI Write command. + * We should find a free slot as we can queue a + * maximum of 32 SCSI commands only. + */ + while (fi->q.free_edb_list[fi->q.edb_buffer_indx] != EDB_FREE) { + update_EDB_indx(fi); + if (fi->q.edb_buffer_indx == initial_indx) { + T_MSG("No free EDB buffers avaliable") + reset_tachyon(fi, SOFTWARE_RESET); + return 1; + } + } + return 0; +} + +static int validate_login(struct fc_info *fi, u_int *base_ptr) +{ +struct fc_node_info *q = fi->node_info_list; +char n_port_name[PORT_NAME_LEN]; +char node_name[NODE_NAME_LEN]; +u_int s_id; + ENTER("validate_login"); + /*index to Port Name in the payload. We need the 8 byte Port Name */ + memcpy(n_port_name, base_ptr + 10, PORT_NAME_LEN); + memcpy(node_name, base_ptr + 12, NODE_NAME_LEN); + s_id = ntohl(*(base_ptr + 3)) & 0x00FFFFFF; + + /* check if Fibre Channel IDs have changed */ + while(q != NULL) { + if (memcmp(n_port_name, q->hw_addr, PORT_NAME_LEN) == 0) { + if ((s_id != q->d_id) || (memcmp(node_name, q->node_name, NODE_NAME_LEN) != 0)) { + DPRINTK1("Fibre Channel ID of Node has changed. Txing LOGO."); + return 0; + } + q->login = LOGIN_COMPLETED; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return 1; + } + q = q->next; + } + DPRINTK1("Port Name does not match. Txing LOGO."); + return 0; + LEAVE("validate_login"); +} + +static void add_to_address_cache(struct fc_info *fi, u_int *base_ptr) +{ +int size = sizeof(struct fc_node_info); +struct fc_node_info *p, *q = fi->node_info_list, *r = NULL; +char n_port_name[PORT_NAME_LEN]; +u_int s_id; + ENTER("add_to_address_cache"); + /*index to Port Name in the payload. We need the 8 byte Port Name */ + memcpy(n_port_name, base_ptr + 13, PORT_NAME_LEN); + s_id = ntohl(*(base_ptr + 3)) & 0x00FFFFFF; + + /* check if info already exists */ + while(q != NULL) { + if (memcmp(n_port_name, q->hw_addr, PORT_NAME_LEN) == 0) { + if (s_id != q->d_id) { + memcpy(&(q->c_of_s[0]), base_ptr + 17, 3 * sizeof(CLASS_OF_SERVICE)); + q->mtu = ntohl(*(base_ptr + 10)) & 0x00000FFF; + q->d_id = s_id; + memcpy(q->node_name, base_ptr + 15, NODE_NAME_LEN); + } + q->login = LOGIN_COMPLETED; + q->scsi = FALSE; + fi->num_nodes++; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + r = q; + q = q->next; + } + p = (struct fc_node_info *)kmalloc(size, GFP_ATOMIC); + if (p == NULL) { + T_MSG("kmalloc failed in add_to_address_cache()"); + return; + } + memcpy(&(p->c_of_s[0]), base_ptr + 17, 3 * sizeof(CLASS_OF_SERVICE)); + p->mtu = ntohl(*(base_ptr + 10)) & 0x00000FFF; + p->d_id = s_id; + memcpy(p->hw_addr, base_ptr + 13, PORT_NAME_LEN); + memcpy(p->node_name, base_ptr + 15, NODE_NAME_LEN); + p->login = LOGIN_COMPLETED; + p->scsi = FALSE; + p->target_id = 0xFF; + p->next = NULL; + if (fi->node_info_list == NULL) + fi->node_info_list = p; + else + r->next = p; + fi->num_nodes++; +#if DEBUG_5526_2 + display_cache(fi); +#endif + LEAVE("add_to_address_cache"); + return; +} + +static void remove_from_address_cache(struct fc_info *fi, u_int *base_ptr, u_int cmnd_code) +{ +struct fc_node_info *q = fi->node_info_list; +u_int s_id; + ENTER("remove_from_address_cache"); + s_id = ntohl(*(base_ptr + 3)) & 0x00FFFFFF; + switch(cmnd_code) { + case ELS_LOGO: + /* check if info exists */ + while (q != NULL) { + if (s_id == q->d_id) { + if (q->login == LOGIN_COMPLETED) + q->login = LOGIN_ATTEMPTED; + if (fi->num_nodes > 0) + fi->num_nodes--; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + q = q->next; + } + DPRINTK1("ELS_LOGO received from node 0x%x which is not logged-in", s_id); + break; + case ELS_RSCN: + { + int payload_len = ntohl(*(base_ptr + 8)) & 0xFF; + int no_of_pages, i; + u_char address_format; + u_short received_ox_id = ntohl(*(base_ptr + 6)) >> 16; + u_int node_id, mask, *page_ptr = base_ptr + 9; + if ((payload_len < 4) || (payload_len > 256)) { + DPRINTK1("RSCN with invalid payload length received"); + tx_ls_rjt(fi, s_id, received_ox_id, LOGICAL_ERR, RECV_FIELD_SIZE); + return; + } + /* Page_size includes the Command Code */ + no_of_pages = (payload_len / 4) - 1; + for (i = 0; i < no_of_pages; i++) { + address_format = ntohl(*page_ptr) >> 24; + node_id = ntohl(*page_ptr) & 0x00FFFFFF; + switch(address_format) { + case PORT_ADDRESS_FORMAT: + rscn_handler(fi, node_id); + break; + case AREA_ADDRESS_FORMAT: + case DOMAIN_ADDRESS_FORMAT: + if (address_format == AREA_ADDRESS_FORMAT) + mask = 0xFFFF00; + else + mask = 0xFF0000; + while(q != NULL) { + if ((q->d_id & mask) == (node_id & mask)) + rscn_handler(fi, q->d_id); + q = q->next; + } + /* There might be some new nodes to be + * discovered. But, some of the earlier + * requests as a result of the RSCN might be + * in progress. We dont want to duplicate that + * effort. So letz call SCR after a lag. + */ + fi->explore_timer.function = scr_timer; + fi->explore_timer.data = (unsigned long)fi; + fi->explore_timer.expires = RUN_AT((no_of_pages*3*HZ)/100); + init_timer(&fi->explore_timer); + add_timer(&fi->explore_timer); + break; + default: + T_MSG("RSCN with invalid address format received"); + tx_ls_rjt(fi, s_id, received_ox_id, LOGICAL_ERR, NO_EXPLN); + } + page_ptr += 1; + } /* end of for loop */ + } /* end of case RSCN: */ + break; + } +#if DEBUG_5526_2 + display_cache(fi); +#endif + LEAVE("remove_from_address_cache"); +} + +static void rscn_handler(struct fc_info *fi, u_int node_id) +{ +struct fc_node_info *q = fi->node_info_list; +int login_state = sid_logged_in(fi, node_id); + if ((login_state == NODE_LOGGED_IN) || (login_state == NODE_PROCESS_LOGGED_IN)) { + while(q != NULL) { + if (q->d_id == node_id) { + q->login = LOGIN_ATTEMPTED; + if (fi->num_nodes > 0) + fi->num_nodes--; + break; + } + else + q = q->next; + } + } + else + if (login_state == NODE_LOGGED_OUT) + tx_adisc(fi, ELS_ADISC, node_id, OX_ID_FIRST_SEQUENCE); + else + if (login_state == NODE_LOGGED_OUT) + tx_logi(fi, ELS_PLOGI, node_id); +} + +static void scr_timer(unsigned long data) +{ +struct fc_info *fi = (struct fc_info *)data; + del_timer(&fi->explore_timer); + tx_name_server_req(fi, FCS_GP_ID4); +} + +static int sid_logged_in(struct fc_info *fi, u_int s_id) +{ +struct fc_node_info *temp = fi->node_info_list; + while(temp != NULL) + if ((temp->d_id == s_id) && (temp->login == LOGIN_COMPLETED)) { + if (temp->scsi != FALSE) + return NODE_PROCESS_LOGGED_IN; + else + return NODE_LOGGED_IN; + } + else + if ((temp->d_id == s_id) && (temp->login != LOGIN_COMPLETED)) + return NODE_LOGGED_OUT; + else + temp = temp->next; + return NODE_NOT_PRESENT; +} + +static void mark_scsi_sid(struct fc_info *fi, u_int *buff_addr, u_char action) +{ +struct fc_node_info *temp = fi->node_info_list; +u_int s_id; +u_int service_params; + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + service_params = ntohl(*(buff_addr + 12)) & 0x000000F0; + while(temp != NULL) + if ((temp->d_id == s_id) && (temp->login == LOGIN_COMPLETED)) { + if (action == DELETE_ENTRY) { + temp->scsi = FALSE; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + /* Check if it is a SCSI Target */ + if (!(service_params & TARGET_FUNC)) { + temp->scsi = INITIATOR; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + temp->scsi = TARGET; + /* This helps to maintain the target_id no matter what your + * Fibre Channel ID is. + */ + if (temp->target_id == 0xFF) { + if (fi->g.no_of_targets <= MAX_SCSI_TARGETS) + temp->target_id = fi->g.no_of_targets++; + else + T_MSG("MAX TARGETS reached!"); + } + else + DPRINTK1("Target_id %d already present", temp->target_id); +#if DEBUG_5526_2 + display_cache(fi); +#endif + return; + } + else + temp = temp->next; + return; +} + +static int node_logged_in_prev(struct fc_info *fi, u_int *buff_addr) +{ +struct fc_node_info *temp; +u_char *data = (u_char *)buff_addr; +u_int s_id; +char node_name[NODE_NAME_LEN]; + s_id = ntohl(*(buff_addr + 3)) & 0x00FFFFFF; + memcpy(node_name, buff_addr + 12, NODE_NAME_LEN); + /* point to port_name in the ADISC payload */ + data += 10 * 4; + /* point to last 6 bytes of port_name */ + data += 2; + temp = look_up_cache(fi, data); + if (temp != NULL) { + if ((temp->d_id == s_id) && (memcmp(node_name, temp->node_name, NODE_NAME_LEN) == 0)) { + temp->login = LOGIN_COMPLETED; +#if DEBUG_5526_2 + display_cache(fi); +#endif + return TRUE; + } + } + return FALSE; +} + +static struct fc_node_info *look_up_cache(struct fc_info *fi, char *data) +{ +struct fc_node_info *temp_list = fi->node_info_list, *q; +u_char n_port_name[FC_ALEN], temp_addr[FC_ALEN]; + ENTER("look_up_cache"); + memcpy(n_port_name, data, FC_ALEN); + while(temp_list) { + if (memcmp(n_port_name, &(temp_list->hw_addr[2]), FC_ALEN) == 0) + return temp_list; + else + temp_list = temp_list->next; + } + + /* Broadcast IP ? + */ + temp_addr[0] = temp_addr[1] = temp_addr[2] = 0xFF; + temp_addr[3] = temp_addr[4] = temp_addr[5] = 0xFF; + if (memcmp(n_port_name, temp_addr, FC_ALEN) == 0) { + q = (struct fc_node_info *)kmalloc(sizeof(struct fc_node_info), GFP_ATOMIC); + if (q == NULL) { + T_MSG("kmalloc failed in look_up_cache()"); + return NULL; + } + q->d_id = BROADCAST; + return q; + } + LEAVE("look_up_cache"); + return NULL; +} + +static int display_cache(struct fc_info *fi) +{ +struct fc_node_info *q = fi->node_info_list; +#if DEBUG_5526_2 +struct ox_id_els_map *temp_ox_id_list = fi->ox_id_list; +#endif +int count = 0, j; + printk("\nFibre Channel Node Information for %s\n", fi->name); + printk("My FC_ID = %x, My WWN = %x %x, ", fi->g.my_id, fi->g.my_node_name_high, fi->g.my_node_name_low); + if (fi->g.ptp_up == TRUE) + printk("Port_Type = N_Port\n"); + if (fi->g.loop_up == TRUE) + printk("Port_Type = L_Port\n"); + while(q != NULL) { + printk("WWN = "); + for (j = 0; j < PORT_NAME_LEN; j++) + printk("%x ", q->hw_addr[j]); + printk("FC_ID = %x, ", q->d_id); + printk("Login = "); + if (q->login == LOGIN_COMPLETED) + printk("ON "); + else + printk("OFF "); + if (q->scsi == TARGET) + printk("Target_ID = %d ", q->target_id); + printk("\n"); + q = q->next; + count++; + } + +#if DEBUG_5526_2 + printk("OX_ID -> ELS Map\n"); + while(temp_ox_id_list) { + printk("ox_id = %x, ELS = %x\n", temp_ox_id_list->ox_id, temp_ox_id_list->els); + temp_ox_id_list = temp_ox_id_list->next; + } +#endif + + return 0; +} + +static struct net_device_stats * iph5526_get_stats(struct device *dev) +{ +struct fc_info *fi = (struct fc_info*)dev->priv; + return (struct net_device_stats *) &fi->fc_stats; +} + + +/* SCSI stuff starts here */ + +static struct proc_dir_entry proc_scsi_iph5526 = { + PROC_SCSI_IPH5526_FC, 7, "iph5526", S_IFDIR, S_IRUGO | S_IXUGO, 2 +}; + + +int iph5526_detect(Scsi_Host_Template *tmpt) +{ +struct Scsi_Host *host = NULL; +struct iph5526_hostdata *hostdata; +struct fc_info *fi = NULL; +int no_of_hosts = 0, timeout, i, j, count = 0; +u_int pci_maddr = 0; +struct pci_dev *pdev = NULL; + + tmpt->proc_dir = &proc_scsi_iph5526; + if (pci_present() == 0) { + printk("iph5526: PCI not present\n"); + return 0; + } + + for (i = 0; i <= MAX_FC_CARDS; i++) + fc[i] = NULL; + + for (i = 0; i < clone_list[i].vendor_id != 0; i++) + while ((pdev = pci_find_device(clone_list[i].vendor_id, clone_list[i].device_id, pdev))) { + unsigned short pci_command; + if (count < MAX_FC_CARDS) { + fc[count] = kmalloc(sizeof(struct fc_info), GFP_ATOMIC); + if (fc[count] == NULL) { + printk("iph5526.c: Unable to register card # %d\n", count + 1); + return no_of_hosts; + } + memset(fc[count], 0, sizeof(struct fc_info)); + } + else { + printk("iph5526.c: Maximum Number of cards reached.\n"); + return no_of_hosts; + } + + fi = fc[count]; + sprintf(fi->name, "fc%d", count); + + host = scsi_register(tmpt, sizeof(struct iph5526_hostdata)); + hostdata = (struct iph5526_hostdata *)host->hostdata; + memset(hostdata, 0 , sizeof(struct iph5526_hostdata)); + for (j = 0; j < MAX_SCSI_TARGETS; j++) + hostdata->tag_ages[j] = jiffies; + hostdata->fi = fi; + fi->host = host; + //host->max_id = MAX_SCSI_TARGETS; + host->max_id = 5; + host->hostt->use_new_eh_code = 1; + host->this_id = tmpt->this_id; + + pci_maddr = pdev->base_address[0]; + if ( (pci_maddr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { + printk("iph5526.c : Cannot find proper PCI device base address.\n"); + scsi_unregister(host); + kfree(fc[count]); + fc[count] = NULL; + continue; + } + + pci_maddr &= PCI_BASE_ADDRESS_MEM_MASK; + DPRINTK("pci_maddr = %x", pci_maddr); + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + + pci_irq_line = pdev->irq; + printk("iph5526.c: PCI BIOS reports %s at i/o %#x, irq %d.\n", clone_list[i].name, pci_maddr, pci_irq_line); + fi->g.mem_base = ioremap(pci_maddr & PAGE_MASK, 1024); + + /* We use Memory Mapped IO. The initial space contains the + * PCI Configuration registers followed by the (i) chip + * registers followed by the Tachyon registers. + */ + /* Thatz where (i)chip maps Tachyon Address Space. + */ + fi->g.tachyon_base = (u_long)fi->g.mem_base + TACHYON_OFFSET + ( pci_maddr & ~PAGE_MASK ); + DPRINTK("fi->g.tachyon_base = %x", (u_int)fi->g.tachyon_base); + if (fi->g.mem_base == NULL) { + printk("iph5526.c : ioremap failed!!!\n"); + scsi_unregister(host); + kfree(fc[count]); + fc[count] = NULL; + continue; + } + DPRINTK("IRQ1 = %d\n", pci_irq_line); + printk(version); + fi->base_addr = (long) pdev; + + if (pci_irq_line) { + int irqval = 0; + /* Found it, get IRQ. + */ + irqval = request_irq(pci_irq_line, &tachyon_interrupt, pci_irq_line ? SA_SHIRQ : 0, fi->name, host); + if (irqval) { + printk("iph5526.c : Unable to get IRQ %d (irqval = %d).\n", pci_irq_line, irqval); + scsi_unregister(host); + kfree(fc[count]); + fc[count] = NULL; + continue; + } + host->irq = fi->irq = pci_irq_line; + pci_irq_line = 0; + fi->clone_id = clone_list[i].vendor_id; + } + + if (!initialize_register_pointers(fi) || !tachyon_init(fi)) { + printk("iph5526.c: TACHYON initialization failed for card # %d!!!\n", count + 1); + free_irq(host->irq, host); + scsi_unregister(host); + if (fi) + clean_up_memory(fi); + kfree(fc[count]); + fc[count] = NULL; + break; + } + DPRINTK1("Fibre Channel card initialized"); + /* Wait for the Link to come up and the login process + * to complete. + */ + for(timeout = jiffies + 10*HZ; (timeout > jiffies) && ((fi->g.link_up == FALSE) || (fi->g.port_discovery == TRUE) || (fi->g.explore_fabric == TRUE) || (fi->g.perform_adisc == TRUE));) + barrier(); + + count++; + no_of_hosts++; + } + DPRINTK1("no_of_hosts = %d",no_of_hosts); + + /* This is to make sure that the ACC to the PRLI comes in + * for the last ALPA. + */ + udelay(1000000); /* Ugly! Let the Gods forgive me */ + + DPRINTK1("leaving iph5526_detect\n"); + return no_of_hosts; +} + + +int iph5526_biosparam(Disk * disk, kdev_t n, int ip[]) +{ +int size = disk->capacity; + ip[0] = 64; + ip[1] = 32; + ip[2] = size >> 11; + if (ip[2] > 1024) { + ip[0] = 255; + ip[1] = 63; + ip[2] = size / (ip[0] * ip[1]); + } + return 0; +} + +int iph5526_queuecommand(Scsi_Cmnd *Cmnd, void (*done) (Scsi_Cmnd *)) +{ +int int_required = 0; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_COMMAND; +u_int type = TYPE_FCP | SEQUENCE_INITIATIVE; +u_int frame_class = Cmnd->target; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +struct Scsi_Host *host = Cmnd->host; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata*)host->hostdata; +struct fc_info *fi = hostdata->fi; +struct fc_node_info *q; +u_long flags; + ENTER("iph5526_queuecommand"); + + spin_lock_irqsave(&fi->fc_lock, flags); + Cmnd->scsi_done = done; + + if (Cmnd->device->tagged_supported) { + switch(Cmnd->tag) { + case SIMPLE_QUEUE_TAG: + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_SIMPLE; + break; + case HEAD_OF_QUEUE_TAG: + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_HEAD_OF_Q; + break; + case ORDERED_QUEUE_TAG: + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_ORDERED; + break; + default: + if ((jiffies - hostdata->tag_ages[Cmnd->target]) > (5 * HZ)) { + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_ORDERED; + hostdata->tag_ages[Cmnd->target] = jiffies; + } + else + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_SIMPLE; + break; + } + } + /*else + hostdata->cmnd.fcp_cntl = FCP_CNTL_QTYPE_UNTAGGED; + */ + + hostdata->cmnd.fcp_addr[3] = 0; + hostdata->cmnd.fcp_addr[2] = 0; + hostdata->cmnd.fcp_addr[1] = 0; + hostdata->cmnd.fcp_addr[0] = htons(Cmnd->lun); + + memcpy(&hostdata->cmnd.fcp_cdb, Cmnd->cmnd, Cmnd->cmd_len); + hostdata->cmnd.fcp_data_len = htonl(Cmnd->request_bufflen); + + /* Get an used OX_ID. We could have pending commands. + */ + if (get_scsi_oxid(fi)) + return 1; + fi->q.free_scsi_oxid[fi->g.scsi_oxid] = OXID_INUSE; + + /* Maintain a handler so that we can associate the done() function + * on completion of the SCSI command. + */ + hostdata->cmnd_handler[fi->g.scsi_oxid] = Cmnd; + + switch(Cmnd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_12: + fi->g.type_of_frame = FC_SCSI_WRITE; + hostdata->cmnd.fcp_cntl = htonl(FCP_CNTL_WRITE | hostdata->cmnd.fcp_cntl); + break; + default: + fi->g.type_of_frame = FC_SCSI_READ; + hostdata->cmnd.fcp_cntl = htonl(FCP_CNTL_READ | hostdata->cmnd.fcp_cntl); + } + + memcpy(fi->q.ptr_fcp_cmnd[fi->q.fcp_cmnd_indx], &(hostdata->cmnd), sizeof(fcp_cmd)); + + q = resolve_target(fi, Cmnd->target); + + if (q == NULL) { + u_int bad_id = fi->g.my_ddaa | 0xFE; + /* We transmit to an non-existant AL_PA so that the "done" + * function can be called while receiving the interrupt + * due to a Timeout for a bad AL_PA. In a PTP configuration, + * the int_required field is set, since there is no notion + * of AL_PAs. This approach sucks, but works alright! + */ + if (fi->g.ptp_up == TRUE) + int_required = 1; + tx_exchange(fi, (char *)(&(hostdata->cmnd)), sizeof(fcp_cmd), r_ctl, type, bad_id, fi->g.my_mtu, int_required, ox_id, FC_SCSI_BAD_TARGET); + spin_unlock_irqrestore(&fi->fc_lock, flags); + DPRINTK1("Target ID %x not present", Cmnd->target); + return 0; + } + if (q->login == LOGIN_COMPLETED) { + if (add_to_sest(fi, Cmnd, q)) { + DPRINTK1("add_to_sest() failed."); + spin_unlock_irqrestore(&fi->fc_lock, flags); + return 0; + } + tx_exchange(fi, (char *)(fi->q.ptr_fcp_cmnd[fi->q.fcp_cmnd_indx]), sizeof(fcp_cmd), r_ctl, type, q->d_id, q->mtu, int_required, ox_id, frame_class << 16); + update_FCP_CMND_indx(fi); + } + spin_unlock_irqrestore(&fi->fc_lock, flags); + /* If q != NULL, then we have a SCSI Target. + * If q->login != LOGIN_COMPLETED, then that device could be + * offline temporarily. So we let the command to time-out. + */ + LEAVE("iph5526_queuecommand"); + return 0; +} + +int iph5526_abort(Scsi_Cmnd *Cmnd) +{ +struct Scsi_Host *host = Cmnd->host; +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata *)host->hostdata; +struct fc_info *fi = hostdata->fi; +struct fc_node_info *q; +u_int r_ctl = FC4_DEVICE_DATA | UNSOLICITED_COMMAND; +u_int type = TYPE_FCP | SEQUENCE_INITIATIVE; +u_short ox_id = OX_ID_FIRST_SEQUENCE; +int int_required = 1, i, abort_status = FALSE; +u_long flags; + + ENTER("iph5526_abort"); + + spin_lock_irqsave(&fi->fc_lock, flags); + + q = resolve_target(fi, Cmnd->target); + if (q == NULL) { + u_int bad_id = fi->g.my_ddaa | 0xFE; + /* This should not happen as we should always be able to + * resolve a target id. But, jus in case... + * We transmit to an non-existant AL_PA so that the done + * function can be called while receiving the interrupt + * for a bad AL_PA. + */ + DPRINTK1("Unresolved Target ID!"); + tx_exchange(fi, (char *)(&(hostdata->cmnd)), sizeof(fcp_cmd), r_ctl, type, bad_id, fi->g.my_mtu, int_required, ox_id, FC_SCSI_BAD_TARGET); + DPRINTK1("Target ID %x not present", Cmnd->target); + spin_unlock_irqrestore(&fi->fc_lock, flags); + return FAILED; + } + + /* If q != NULL, then we have a SCSI Target. If + * q->login != LOGIN_COMPLETED, then that device could + * be offline temporarily. So we let the command to time-out. + */ + + /* Get the OX_ID for the Command to be aborted. + */ + for (i = 0; i <= MAX_SCSI_XID; i++) { + if (hostdata->cmnd_handler[i] == Cmnd) { + hostdata->cmnd_handler[i] = NULL; + ox_id = i; + break; + } + } + if (i > MAX_SCSI_XID) { + T_MSG("Command could not be resolved to OX_ID"); + spin_unlock_irqrestore(&fi->fc_lock, flags); + return FAILED; + } + + switch(Cmnd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_12: + break; + default: + ox_id |= SCSI_READ_BIT; + } + abort_status = abort_exchange(fi, ox_id); + + if ((q->login == LOGIN_COMPLETED) && (abort_status == TRUE)) { + /* Then, transmit an ABTS to the target. The rest + * is done when the BA_ACC is received for the ABTS. + */ + tx_abts(fi, q->d_id, ox_id); + } + else { + u_int STE_bit; + u_short x_id; + /* Invalidate resources for that Exchange. + */ + x_id = ox_id & MAX_SCSI_XID; + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + if (STE_bit & SEST_V) { + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + invalidate_SEST_entry(fi, ox_id); + } + } + + LEAVE("iph5526_abort"); + spin_unlock_irqrestore(&fi->fc_lock, flags); + return SUCCESS; +} + +static int abort_exchange(struct fc_info *fi, u_short ox_id) +{ +u_short x_id; +volatile u_int flush_SEST, STE_bit; + x_id = ox_id & MAX_SCSI_XID; + DPRINTK1("Aborting Exchange %x", ox_id); + + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + /* Is the Exchange still active?. + */ + if (STE_bit & SEST_V) { + if (ox_id & SCSI_READ_BIT) { + /* If the Exchange to be aborted is Inbound, + * Flush the SEST Entry from Tachyon's Cache. + */ + *(fi->q.ptr_sest[x_id]) &= htonl(SEST_INV); + flush_tachyon_cache(fi, ox_id); + flush_SEST = readl(fi->t_r.ptr_tach_flush_oxid_reg); + while ((flush_SEST & 0x80000000) != 0) + flush_SEST = readl(fi->t_r.ptr_tach_flush_oxid_reg); + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + while ((STE_bit & 0x80000000) != 0) + STE_bit = ntohl(*fi->q.ptr_sest[x_id]); + flush_SEST = readl(fi->t_r.ptr_tach_flush_oxid_reg); + invalidate_SEST_entry(fi, ox_id); + } + else { + int i; + u_int *ptr_edb; + /* For In-Order Reassembly, the following is done: + * First, write zero as the buffer length in the EDB. + */ + ptr_edb = bus_to_virt(ntohl(*(fi->q.ptr_sest[x_id] + 7))); + for (i = 0; i < EDB_LEN; i++) + if (fi->q.ptr_edb[i] == ptr_edb) + break; + if (i < EDB_LEN) + *ptr_edb = *ptr_edb & 0x0000FFFF; + else + T_MSG("EDB not found while clearing in abort_exchange()"); + } + DPRINTK1("Exchange %x invalidated", ox_id); + return TRUE; + } + else { + DPRINTK1("SEST Entry for exchange %x not valid", ox_id); + return FALSE; + } +} + +static void flush_tachyon_cache(struct fc_info *fi, u_short ox_id) +{ +volatile u_int tachyon_status; + if (fi->g.loop_up == TRUE) { + writel(HOST_CONTROL, fi->t_r.ptr_fm_control_reg); + /* Make sure that the Inbound FIFO is empty. + */ + do { + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + udelay(200); + }while ((tachyon_status & RECEIVE_FIFO_EMPTY) == 0); + /* Ok. Go ahead and flushhhhhhhhh! + */ + writel(0x80000000 | ox_id, fi->t_r.ptr_tach_flush_oxid_reg); + writel(EXIT_HOST_CONTROL, fi->t_r.ptr_fm_control_reg); + return; + } + if (fi->g.ptp_up == TRUE) { + take_tachyon_offline(fi); + /* Make sure that the Inbound FIFO is empty. + */ + do { + tachyon_status = readl(fi->t_r.ptr_tach_status_reg); + udelay(200); + }while ((tachyon_status & RECEIVE_FIFO_EMPTY) == 0); + writel(0x80000000 | ox_id, fi->t_r.ptr_tach_flush_oxid_reg); + /* Write the Initialize command to the FM Control reg. + */ + fi->g.n_port_try = TRUE; + DPRINTK1("In abort_exchange, TACHYON initializing as N_Port...\n"); + writel(INITIALIZE, fi->t_r.ptr_fm_control_reg); + } +} + +static struct fc_node_info *resolve_target(struct fc_info *fi, u_char target) +{ +struct fc_node_info *temp = fi->node_info_list; + while(temp != NULL) + if (temp->target_id == target) { + if ((temp->scsi == TARGET) && (temp->login == LOGIN_COMPLETED)) + return temp; + else { + if (temp->login != LOGIN_COMPLETED) { + /* The Target is not currently logged in. + * It could be a Target on the Local Loop or + * on a Remote Loop connected through a switch. + * In either case, we will know whenever the Target + * comes On-Line again. We let the command to + * time-out so that it gets retried. + */ + T_MSG("Target %d not logged in.", temp->target_id); + tx_logi(fi, ELS_PLOGI, temp->d_id); + return temp; + } + else { + if (temp->scsi != TARGET) { + /* For some reason, we did not get a response to + * PRLI. Letz try it again... + */ + DPRINTK1("Node not PRLIied. Txing PRLI..."); + tx_prli(fi, ELS_PRLI, temp->d_id, OX_ID_FIRST_SEQUENCE); + } + } + return temp; + } + } + else + temp = temp->next; + return NULL; +} + +static int add_to_sest(struct fc_info *fi, Scsi_Cmnd *Cmnd, struct fc_node_info *ni) +{ +/* we have at least 1 buffer, the terminator */ +int no_of_sdb_buffers = 1, i; +int no_of_edb_buffers = 0; +u_int *req_buffer = (u_int *)Cmnd->request_buffer; +u_int *ptr_sdb = NULL; +struct scatterlist *sl1, *sl2 = NULL; +int no_of_sg = 0; + + switch(fi->g.type_of_frame) { + case FC_SCSI_READ: + fi->g.inb_sest_entry.flags_and_byte_offset = htonl(INB_SEST_VED); + fi->g.inb_sest_entry.byte_count = 0; + fi->g.inb_sest_entry.no_of_recvd_frames = 0; + fi->g.inb_sest_entry.no_of_expected_frames = 0; + fi->g.inb_sest_entry.last_fctl = 0; + + if (Cmnd->use_sg) { + no_of_sg = Cmnd->use_sg; + sl1 = sl2 = (struct scatterlist *)Cmnd->request_buffer; + for (i = 0; i < no_of_sg; i++) { + no_of_sdb_buffers += sl1->length / SEST_BUFFER_SIZE; + if (sl1->length % SEST_BUFFER_SIZE) + no_of_sdb_buffers++; + sl1++; + } + } + else { + no_of_sdb_buffers += Cmnd->request_bufflen / SEST_BUFFER_SIZE; + if (Cmnd->request_bufflen % SEST_BUFFER_SIZE) + no_of_sdb_buffers++; + } /* if !use_sg */ + + /* We are working with the premise that at the max we would + * get a scatter-gather buffer containing 63 buffers + * of size 1024 bytes each. Is it a _bad_ assumption? + */ + if (no_of_sdb_buffers > 512) { + T_MSG("Number of SDB buffers needed = %d", no_of_sdb_buffers); + T_MSG("Disable Scatter-Gather!!!"); + return 1; + } + + + /* Store it in the sdb_table so that we can retrieve that + * free up the memory when the Read Command completes. + */ + if (get_free_SDB(fi)) + return 1; + ptr_sdb = fi->q.ptr_sdb_slot[fi->q.sdb_indx]; + fi->q.sdb_slot_status[fi->q.sdb_indx] = SDB_BUSY; + fi->g.inb_sest_entry.sdb_address = htonl(virt_to_bus(ptr_sdb)); + + if (Cmnd->use_sg) { + int count = 0, j; + for(i = 0; i < no_of_sg; i++) { + char *addr_ptr = sl2->address; + count = sl2->length / SEST_BUFFER_SIZE; + if (sl2->length % SEST_BUFFER_SIZE) + count++; + for (j = 0; j < count; j++) { + *(ptr_sdb) = htonl(virt_to_bus(addr_ptr)); + addr_ptr += SEST_BUFFER_SIZE; + ptr_sdb++; + } + count = 0; + sl2++; + } + } + else { + for (i = 0; i < no_of_sdb_buffers - 1; i++) { + *(ptr_sdb) = htonl(virt_to_bus(req_buffer)); + req_buffer += SEST_BUFFER_SIZE/4; + ptr_sdb++; + } + } + *(ptr_sdb) = htonl(0x1); /* Terminator */ + + /* The scratch pad is used to hold the index into the SDB. + */ + fi->g.inb_sest_entry.scratch_pad = fi->q.sdb_indx; + fi->g.inb_sest_entry.expected_ro = 0; + fi->g.inb_sest_entry.buffer_index = 0; + fi->g.inb_sest_entry.buffer_offset = 0; + memcpy(fi->q.ptr_sest[fi->g.scsi_oxid], &fi->g.inb_sest_entry, sizeof(INB_SEST_ENTRY)); + break; + case FC_SCSI_WRITE: + fi->g.outb_sest_entry.flags_and_did = htonl(OUTB_SEST_VED | ni->d_id); + fi->g.outb_sest_entry.max_frame_len = htons(ni->mtu << 4); + fi->g.outb_sest_entry.cntl = htons(ODB_CLASS_3 | ODB_EE_CREDIT | ODB_NO_INT | ODB_NO_COMP); + fi->g.outb_sest_entry.total_seq_length = INV_SEQ_LEN; + fi->g.outb_sest_entry.link = htons(OUTB_SEST_LINK); + fi->g.outb_sest_entry.transaction_id = htonl(fi->g.scsi_oxid); + fi->g.outb_sest_entry.seq_id = fi->g.seq_id; + fi->g.outb_sest_entry.reserved = 0x0; + fi->g.outb_sest_entry.header_length = htons(TACHYON_HEADER_LEN); + + { + u_char df_ctl = 0; + u_short rx_id = RX_ID_FIRST_SEQUENCE; + u_int r_ctl = FC4_DEVICE_DATA | SOLICITED_DATA; + u_int type = TYPE_FCP | SEQUENCE_INITIATIVE; + /* Multi Frame Sequence ? If yes, set RO bit. + */ + if (Cmnd->request_bufflen > ni->mtu) + type |= RELATIVE_OFF_PRESENT; + build_tachyon_header(fi, fi->g.my_id, r_ctl, ni->d_id, type, fi->g.seq_id, df_ctl, fi->g.scsi_oxid, rx_id, NULL); + if (get_free_header(fi) || get_free_EDB(fi)) + return 1; + memcpy(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx], &(fi->g.tach_header), TACHYON_HEADER_LEN); + fi->g.outb_sest_entry.header_address = htonl(virt_to_bus(fi->q.ptr_tachyon_header[fi->q.tachyon_header_indx])); + update_tachyon_header_indx(fi); + } + + if (Cmnd->use_sg) { + no_of_sg = Cmnd->use_sg; + sl1 = sl2 = (struct scatterlist *)Cmnd->request_buffer; + for (i = 0; i < no_of_sg; i++) { + no_of_edb_buffers += sl1->length / SEST_BUFFER_SIZE; + if (sl1->length % SEST_BUFFER_SIZE) + no_of_edb_buffers++; + sl1++; + } + } + else { + no_of_edb_buffers += Cmnd->request_bufflen / SEST_BUFFER_SIZE; + if (Cmnd->request_bufflen % SEST_BUFFER_SIZE) + no_of_edb_buffers++; + } /* if !use_sg */ + + + /* We need "no_of_edb_buffers" _contiguous_ EDBs + * that are FREE. Check for that first. + */ + for (i = 0; i < no_of_edb_buffers; i++) { + int j; + if ((fi->q.edb_buffer_indx + no_of_edb_buffers) >= EDB_LEN) + fi->q.edb_buffer_indx = 0; + if (fi->q.free_edb_list[fi->q.edb_buffer_indx + i] != EDB_FREE) { + for (j = 0; j < i; j++) + update_EDB_indx(fi); + if (get_free_EDB(fi)) + return 1; + i = 0; + } + } + + /* We got enuff FREE EDBs. + */ + if (Cmnd->use_sg) { + fi->g.outb_sest_entry.edb_address = htonl(virt_to_bus(fi->q.ptr_edb[fi->q.edb_buffer_indx])); + sl1 = (struct scatterlist *)Cmnd->request_buffer; + for(i = 0; i < no_of_sg; i++) { + int count = 0, j; + count = sl1->length / SEST_BUFFER_SIZE; + for (j = 0; j < count; j++) { + build_EDB(fi, (char *)sl1->address, 0, SEST_BUFFER_SIZE); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + /* Mark this EDB as being in use */ + fi->q.free_edb_list[fi->q.edb_buffer_indx] = EDB_BUSY; + /* We have already made sure that we have enuff + * free EDBs that are contiguous. So this is + * safe. + */ + update_EDB_indx(fi); + sl1->address += SEST_BUFFER_SIZE; + } + /* Just in case itz not a multiple of + * SEST_BUFFER_SIZE bytes. + */ + if (sl1->length % SEST_BUFFER_SIZE) { + build_EDB(fi, (char *)sl1->address, 0, sl1->length % SEST_BUFFER_SIZE); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + fi->q.free_edb_list[fi->q.edb_buffer_indx] = EDB_BUSY; + update_EDB_indx(fi); + } + sl1++; + } + /* The last EDB is special. It needs the "end bit" to + * be set. + */ + *(fi->q.ptr_edb[fi->q.edb_buffer_indx - 1] + 1) = *(fi->q.ptr_edb[fi->q.edb_buffer_indx - 1] + 1) | ntohs(EDB_END); + } + else { + int count = 0, j; + fi->g.outb_sest_entry.edb_address = htonl(virt_to_bus(fi->q.ptr_edb[fi->q.edb_buffer_indx])); + count = Cmnd->request_bufflen / SEST_BUFFER_SIZE; + for (j = 0; j < count; j++) { + build_EDB(fi, (char *)req_buffer, 0, SEST_BUFFER_SIZE); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + /* Mark this EDB as being in use */ + fi->q.free_edb_list[fi->q.edb_buffer_indx] = EDB_BUSY; + /* We have already made sure that we have enuff + * free EDBs that are contiguous. So this is + * safe. + */ + update_EDB_indx(fi); + req_buffer += SEST_BUFFER_SIZE; + } + /* Just in case itz not a multiple of + * SEST_BUFFER_SIZE bytes. + */ + if (Cmnd->request_bufflen % SEST_BUFFER_SIZE) { + build_EDB(fi, (char *)req_buffer, EDB_END, Cmnd->request_bufflen % SEST_BUFFER_SIZE); + memcpy(fi->q.ptr_edb[fi->q.edb_buffer_indx], &(fi->g.edb), sizeof(EDB)); + fi->q.free_edb_list[fi->q.edb_buffer_indx] = EDB_BUSY; + update_EDB_indx(fi); + } + else { + /* Mark the last EDB as the "end edb". + */ + *(fi->q.ptr_edb[fi->q.edb_buffer_indx - 1] + 1) = *(fi->q.ptr_edb[fi->q.edb_buffer_indx - 1] + 1) | htons(EDB_END); + } + } + + /* Finally we have something to send!. + */ + memcpy(fi->q.ptr_sest[fi->g.scsi_oxid], &fi->g.outb_sest_entry, sizeof(OUTB_SEST_ENTRY)); + break; + } + return 0; +} + +static void update_FCP_CMND_indx(struct fc_info *fi) +{ + fi->q.fcp_cmnd_indx++; + if (fi->q.fcp_cmnd_indx == NO_OF_FCP_CMNDS) + fi->q.fcp_cmnd_indx = 0; +} + +static int get_scsi_oxid(struct fc_info *fi) +{ +u_short initial_oxid = fi->g.scsi_oxid; + /* Check if the OX_ID is in use. + * We could have an outstanding SCSI command. + */ + while (fi->q.free_scsi_oxid[fi->g.scsi_oxid] != OXID_AVAILABLE) { + update_scsi_oxid(fi); + if (fi->g.scsi_oxid == initial_oxid) { + T_MSG("No free OX_IDs avaliable") + reset_tachyon(fi, SOFTWARE_RESET); + return 1; + } + } + return 0; +} + +static void update_scsi_oxid(struct fc_info *fi) +{ + fi->g.scsi_oxid++; + if (fi->g.scsi_oxid == (MAX_SCSI_XID + 1)) + fi->g.scsi_oxid = 0; +} + +static int get_free_SDB(struct fc_info *fi) +{ +unsigned int initial_indx = fi->q.sdb_indx; + /* Check if the SDB is in use. + * We could have an outstanding SCSI Read command. + * We should find a free slot as we can queue a + * maximum of 32 SCSI commands only. + */ + while (fi->q.sdb_slot_status[fi->q.sdb_indx] != SDB_FREE) { + update_SDB_indx(fi); + if (fi->q.sdb_indx == initial_indx) { + T_MSG("No free SDB buffers avaliable") + reset_tachyon(fi, SOFTWARE_RESET); + return 1; + } + } + return 0; +} + +static void update_SDB_indx(struct fc_info *fi) +{ + fi->q.sdb_indx++; + if (fi->q.sdb_indx == NO_OF_SDB_ENTRIES) + fi->q.sdb_indx = 0; +} + +int iph5526_release(struct Scsi_Host *host) +{ +struct iph5526_hostdata *hostdata = (struct iph5526_hostdata*)host->hostdata; +struct fc_info *fi = hostdata->fi; + free_irq(host->irq, host); + iounmap(fi->g.mem_base); + return 0; +} + +const char *iph5526_info(struct Scsi_Host *host) +{ +static char buf[80]; + sprintf(buf, "Interphase 5526 Fibre Channel PCI SCSI Adapter using IRQ %d\n", host->irq); + return buf; +} + +#ifdef MODULE + +#define NAMELEN 8 /* # of chars for storing dev->name */ + +static struct device *dev_fc[MAX_FC_CARDS]; + +static int io = 0; +static int irq = 0; +static int bad = 0; /* 0xbad = bad sig or no reset ack */ +static int scsi_registered; + + +int init_module(void) +{ +int i = 0; + + driver_template.module = &__this_module; + scsi_register_module(MODULE_SCSI_HA, &driver_template); + if (driver_template.present) + scsi_registered = TRUE; + else { + printk("iph5526: SCSI registeration failed!!!\n"); + scsi_registered = FALSE; + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); + } + + while(fc[i] != NULL) { + dev_fc[i] = NULL; + dev_fc[i] = init_fcdev(dev_fc[i], 0); + if (dev_fc[i] == NULL) { + printk("iph5526.c: init_fcdev failed for card #%d\n", i+1); + break; + } + dev_fc[i]->irq = irq; + dev_fc[i]->mem_end = bad; + dev_fc[i]->base_addr = io; + dev_fc[i]->init = iph5526_probe; + dev_fc[i]->priv = fc[i]; + fc[i]->dev = dev_fc[i]; + if (register_fcdev(dev_fc[i]) != 0) { + kfree_s(dev_fc[i], sizeof(struct device)); + dev_fc[i] = NULL; + if (i == 0) { + printk("iph5526.c: IP registeration failed!!!\n"); + return -ENODEV; + } + } + i++; + } + if (i == 0) + return -ENODEV; + + return 0; +} + +void cleanup_module(void) +{ +int i = 0; + while(fc[i] != NULL) { + struct device *dev = fc[i]->dev; + void *priv = dev->priv; + fc[i]->g.dont_init = TRUE; + take_tachyon_offline(fc[i]); + unregister_fcdev(dev); + clean_up_memory(fc[i]); + if (dev->priv) + kfree(priv); + kfree(dev); + dev = NULL; + i++; + } + if (scsi_registered == TRUE) + scsi_unregister_module(MODULE_SCSI_HA, &driver_template); +} +#endif /* MODULE */ + +void clean_up_memory(struct fc_info *fi) +{ +int i,j; + ENTER("clean_up_memory"); + if (fi->q.ptr_mfsbq_base) + free_pages((u_long)bus_to_virt(ntohl(*(fi->q.ptr_mfsbq_base))), 5); + DPRINTK("after kfree2"); + for (i = 0; i < SFSBQ_LENGTH; i++) + for (j = 0; j < NO_OF_ENTRIES; j++) + if (fi->q.ptr_sfs_buffers[i*NO_OF_ENTRIES + j]) + kfree(fi->q.ptr_sfs_buffers[i*NO_OF_ENTRIES + j]); + DPRINTK("after kfree1"); + if (fi->q.ptr_ocq_base) + free_page((u_long)fi->q.ptr_ocq_base); + if (fi->q.ptr_imq_base) + free_page((u_long)fi->q.ptr_imq_base); + if (fi->q.ptr_mfsbq_base) + free_page((u_long)fi->q.ptr_mfsbq_base); + if (fi->q.ptr_sfsbq_base) + free_page((u_long)fi->q.ptr_sfsbq_base); + if (fi->q.ptr_edb_base) + free_pages((u_long)fi->q.ptr_edb_base, 5); + if (fi->q.ptr_sest_base) + free_pages((u_long)fi->q.ptr_sest_base, 5); + if (fi->q.ptr_tachyon_header_base) + free_page((u_long)fi->q.ptr_tachyon_header_base); + if (fi->q.ptr_sdb_base) + free_pages((u_long)fi->q.ptr_sdb_base, 5); + if (fi->q.ptr_fcp_cmnd_base) + free_page((u_long)fi->q.ptr_fcp_cmnd_base); + DPRINTK("after free_pages"); + if (fi->q.ptr_host_ocq_cons_indx) + kfree(fi->q.ptr_host_ocq_cons_indx); + if (fi->q.ptr_host_hpcq_cons_indx) + kfree(fi->q.ptr_host_hpcq_cons_indx); + if (fi->q.ptr_host_imq_prod_indx) + kfree(fi->q.ptr_host_imq_prod_indx); + DPRINTK("after kfree3"); + while (fi->node_info_list) { + struct fc_node_info *temp_list = fi->node_info_list; + fi->node_info_list = fi->node_info_list->next; + kfree(temp_list); + } + while (fi->ox_id_list) { + struct ox_id_els_map *temp = fi->ox_id_list; + fi->ox_id_list = fi->ox_id_list->next; + kfree(temp); + } + LEAVE("clean_up_memory"); +} + +static int initialize_register_pointers(struct fc_info *fi) +{ +ENTER("initialize_register_pointers"); +if(fi->g.tachyon_base == 0) + return -ENOMEM; + +fi->i_r.ptr_ichip_hw_control_reg = ICHIP_HW_CONTROL_REG_OFF + fi->g.tachyon_base; +fi->i_r.ptr_ichip_hw_status_reg = ICHIP_HW_STATUS_REG_OFF + fi->g.tachyon_base; +fi->i_r.ptr_ichip_hw_addr_mask_reg = ICHIP_HW_ADDR_MASK_REG_OFF + fi->g.tachyon_base; +fi->t_r.ptr_ocq_base_reg = OCQ_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_ocq_len_reg = OCQ_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_ocq_prod_indx_reg = OCQ_PRODUCER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_ocq_cons_indx_reg = OCQ_CONSUMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_imq_base_reg = IMQ_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_imq_len_reg = IMQ_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_imq_cons_indx_reg = IMQ_CONSUMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_imq_prod_indx_reg = IMQ_PRODUCER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbq_base_reg = MFSBQ_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbq_len_reg = MFSBQ_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbq_prod_reg = MFSBQ_PRODUCER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbq_cons_reg = MFSBQ_CONSUMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_mfsbuff_len_reg = MFS_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbq_base_reg = SFSBQ_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbq_len_reg = SFSBQ_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbq_prod_reg = SFSBQ_PRODUCER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbq_cons_reg = SFSBQ_CONSUMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sfsbuff_len_reg = SFS_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sest_base_reg = SEST_BASE_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_sest_len_reg = SEST_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_scsibuff_len_reg = SCSI_LENGTH_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_tach_config_reg = TACHYON_CONFIG_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_tach_control_reg = TACHYON_CONTROL_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_tach_status_reg = TACHYON_STATUS_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_tach_flush_oxid_reg = TACHYON_FLUSH_SEST_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_config_reg = FMGR_CONFIG_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_control_reg = FMGR_CONTROL_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_status_reg = FMGR_STATUS_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_tov_reg = FMGR_TIMER_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_wwn_hi_reg = FMGR_WWN_HI_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_wwn_low_reg = FMGR_WWN_LO_REGISTER_OFFSET + fi->g.tachyon_base; +fi->t_r.ptr_fm_rx_al_pa_reg = FMGR_RCVD_ALPA_REGISTER_OFFSET + fi->g.tachyon_base; + +LEAVE("initialize_register_pointers"); +return 1; +} + + + +/* + * Local variables: + * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c iph5526.c" + * version-control: t + * kept-new-versions: 5 + * End: + */ diff -u --recursive --new-file v2.2.11/linux/drivers/net/fc/iph5526_ip.h linux/drivers/net/fc/iph5526_ip.h --- v2.2.11/linux/drivers/net/fc/iph5526_ip.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/fc/iph5526_ip.h Wed Aug 25 17:29:48 1999 @@ -0,0 +1,25 @@ +#ifndef IPH5526_IP_H +#define IPH5526_IP_H + +#define LLC_SNAP_LEN 0x8 + +/* Offsets into the ARP frame */ +#define ARP_OPCODE_0 (0x6 + LLC_SNAP_LEN) +#define ARP_OPCODE_1 (0x7 + LLC_SNAP_LEN) + +int iph5526_probe(struct device *dev); +static int fcdev_init(struct device *dev); +static int iph5526_open(struct device *dev); +static int iph5526_close(struct device *dev); +static int iph5526_send_packet(struct sk_buff *skb, struct device *dev); +static struct net_device_stats * iph5526_get_stats(struct device *dev); +static int iph5526_change_mtu(struct device *dev, int mtu); + + +static void rx_net_packet(struct fc_info *fi, u_char *buff_addr, int payload_size); +static void rx_net_mfs_packet(struct fc_info *fi, struct sk_buff *skb); +unsigned short fc_type_trans(struct sk_buff *skb, struct device *dev); +static int tx_ip_packet(struct sk_buff *skb, unsigned long len, struct fc_info *fi); +static int tx_arp_packet(char *data, unsigned long len, struct fc_info *fi); +#endif + diff -u --recursive --new-file v2.2.11/linux/drivers/net/fc/iph5526_novram.c linux/drivers/net/fc/iph5526_novram.c --- v2.2.11/linux/drivers/net/fc/iph5526_novram.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/fc/iph5526_novram.c Wed Aug 25 17:29:48 1999 @@ -0,0 +1,278 @@ +/********************************************************************** + * Reading the NVRAM on the Interphase 5526 PCI Fibre Channel Card. + * All contents in this file : courtesy Interphase Corporation. + * Special thanks to Kevin Quick, kquick@iphase.com. + **********************************************************************/ + +#define FF_MAGIC 0x4646 +#define DB_MAGIC 0x4442 +#define DL_MAGIC 0x444d + + +#define CMD_LEN 9 + +/*********** + * + * Switches and defines for header files. + * + * The following defines are used to turn on and off + * various options in the header files. Primarily useful + * for debugging. + * + ***********/ + +static const unsigned short novram_default[4] = { + FF_MAGIC, + DB_MAGIC, + DL_MAGIC, + 0 }; + + +/* + * a list of the commands that can be sent to the NOVRAM + */ + +#define NR_EXTEND 0x100 +#define NR_WRITE 0x140 +#define NR_READ 0x180 +#define NR_ERASE 0x1c0 + +#define EWDS 0x00 +#define WRAL 0x10 +#define ERAL 0x20 +#define EWEN 0x30 + +/* + * Defines for the pins on the NOVRAM + */ + +#define BIT(x) (1 << (x)) + +#define NVDI_B 31 +#define NVDI BIT(NVDI_B) +#define NVDO BIT(9) +#define NVCE BIT(30) +#define NVSK BIT(29) +#define NV_MANUAL BIT(28) + +/*********** + * + * Include files. + * + ***********/ + +#define KeStallExecutionProcessor(x) {volatile int d, p;\ + for (d=0; dn_r.ptr_novram_hw_control_reg); \ + t &= (val); \ + writel(t, fi->n_r.ptr_novram_hw_control_reg); \ + } + +/*********************** + * + * This define ors the value and the current config register and puts + * the result in the config register + * + ***********************/ + +#define CFG_OR(val) { volatile int t; \ + t = readl(fi->n_r.ptr_novram_hw_control_reg); \ + t |= (val); \ + writel(t, fi->n_r.ptr_novram_hw_control_reg); \ + } + +/*********************** + * + * Send a command to the NOVRAM, the command is in cmd. + * + * clear CE and SK. Then assert CE. + * Clock each of the command bits out in the correct order with SK + * exit with CE still asserted + * + ***********************/ + +#define NVRAM_CMD(cmd) { int i; \ + int c = cmd; \ + CFG_AND(~(NVCE|NVSK)); \ + CFG_OR(NVCE); \ + for (i=0; in_r.ptr_novram_hw_status_reg) & NVDO) ? 1 : 0; \ + } + +/*********** + * + * Function Prototypes + * + ***********/ + +static int iph5526_nr_get(struct fc_info *fi, int addr); +static void iph5526_nr_do_init(struct fc_info *fi); +static void iph5526_nr_checksum(struct fc_info *fi); + + +/******************************************************************* + * + * Local routine: iph5526_nr_do_init + * Purpose: initialize novram server + * Description: + * + * iph5526_nr_do_init reads the novram into the temporary holding place. + * A checksum is done on the area and the Magic Cookies are checked. + * If any of them are bad, the NOVRAM is initialized with the + * default values and a warning message is displayed. + * + *******************************************************************/ + +static void iph5526_nr_do_init(struct fc_info *fi) +{ + int i; + unsigned short chksum = 0; + int bad = 0; + + for (i=0; in_r.data[i] = iph5526_nr_get(fi, i); + chksum += fi->n_r.data[i]; + } + + if (chksum) + bad = 1; + + if (fi->n_r.data[IPH5526_NOVRAM_SIZE - 4] != FF_MAGIC) + bad = 1; + if (fi->n_r.data[IPH5526_NOVRAM_SIZE - 3] != DB_MAGIC) + bad = 1; + if (fi->n_r.data[IPH5526_NOVRAM_SIZE - 2] != DL_MAGIC) + bad = 1; + + if (bad) { + for (i=0; in_r.data[i] = 0xffff; + } else { + fi->n_r.data[i] = novram_default[i - (IPH5526_NOVRAM_SIZE - 4)]; + } + } + iph5526_nr_checksum(fi); + } +} + + +/******************************************************************* + * + * Local routine: iph5526_nr_get + * Purpose: read a single word of NOVRAM + * Description: + * + * read the 16 bits that make up a word addr of the novram. + * The 16 bits of data that are read are returned as the return value + * + *******************************************************************/ + +static int iph5526_nr_get(struct fc_info *fi, int addr) +{ + int i; + int t; + int val = 0; + + CFG_OR(NV_MANUAL); + + /* + * read the first bit that was clocked with the falling edge of the + * the last command data clock + */ + + NVRAM_CMD(NR_READ + addr); + + /* + * Now read the rest of the bits, the next bit read is D1, then D2, + * and so on + */ + + val = 0; + for (i=0; i<16; i++) { + NVRAM_CLKIN(t); + val <<= 1; + val |= t; + } + NVRAM_CLR_CE; + + CFG_OR(NVDI); + CFG_AND(~NV_MANUAL); + + return(val); +} + + + + +/******************************************************************* + * + * Local routine: iph5526_nr_checksum + * Purpose: calculate novram checksum on fi->n_r.data + * Description: + * + * calculate a checksum for the novram on the image that is + * currently in fi->n_r.data + * + *******************************************************************/ + +static void iph5526_nr_checksum(struct fc_info *fi) +{ + int i; + unsigned short chksum = 0; + + for (i=0; i<(IPH5526_NOVRAM_SIZE - 1); i++) + chksum += fi->n_r.data[i]; + + fi->n_r.data[i] = -chksum; +} diff -u --recursive --new-file v2.2.11/linux/drivers/net/fc/iph5526_scsi.h linux/drivers/net/fc/iph5526_scsi.h --- v2.2.11/linux/drivers/net/fc/iph5526_scsi.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/fc/iph5526_scsi.h Wed Aug 25 17:29:48 1999 @@ -0,0 +1,31 @@ +#ifndef IPH5526_SCSI_H +#define IPH5526_SCSI_H + +#define IPH5526_CAN_QUEUE 32 +#define IPH5526_SCSI_FC { \ + name: "Interphase 5526 Fibre Channel SCSI Adapter", \ + detect: iph5526_detect, \ + release: iph5526_release, \ + info: iph5526_info, \ + queuecommand: iph5526_queuecommand, \ + bios_param: iph5526_biosparam, \ + can_queue: IPH5526_CAN_QUEUE, \ + this_id: -1, \ + sg_tablesize: 255, \ + cmd_per_lun: 8, \ + use_clustering: DISABLE_CLUSTERING, \ + eh_abort_handler: iph5526_abort, \ + eh_device_reset_handler:NULL, \ + eh_bus_reset_handler: NULL, \ + eh_host_reset_handler: NULL, \ +} + +int iph5526_detect(Scsi_Host_Template *tmpt); +int iph5526_queuecommand(Scsi_Cmnd *Cmnd, void (*done) (Scsi_Cmnd *)); +int iph5526_release(struct Scsi_Host *host); +int iph5526_abort(Scsi_Cmnd *Cmnd); +const char *iph5526_info(struct Scsi_Host *host); +int iph5526_biosparam(Disk * disk, kdev_t n, int ip[]); + +#endif + diff -u --recursive --new-file v2.2.11/linux/drivers/net/fc/tach.h linux/drivers/net/fc/tach.h --- v2.2.11/linux/drivers/net/fc/tach.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/fc/tach.h Wed Aug 25 17:29:48 1999 @@ -0,0 +1,475 @@ +/********************************************************************** + * Defines for the Tachyon Fibre Channel Controller and the Interphase + * (i)chip TPI. + *********************************************************************/ + +#ifndef _TACH_H +#define _TACH_H + +#define MY_PAGE_SIZE 4096 +#define REPLICATE 0xFF +#define MAX_NODES 127 +#define BROADCAST 0xFFFFFF +#define BROADCAST_ADDR 0xFFFFFFFFFFFF +#define LOGIN_COMPLETED 2 +#define LOGIN_ATTEMPTED 1 +#define LOGIN_NOT_ATTEMPTED 0 +#define TRUE 1 +#define FALSE 0 + +#define TACHYON_LIMIT 0x01EF +#define TACHYON_OFFSET 0x200 + +/* Offsets to the (i) chip */ +#define ICHIP_HW_CONTROL_REG_OFF (0x080 - TACHYON_OFFSET) +#define ICHIP_HW_STATUS_REG_OFF (0x084 - TACHYON_OFFSET) +#define ICHIP_HW_ADDR_MASK_REG_OFF (0x090 - TACHYON_OFFSET) + +/* (i)chip Hardware Control Register defines */ +#define ICHIP_HCR_RESET 0x01 +#define ICHIP_HCR_DERESET 0x0 +#define ICHIP_HCR_ENABLE_INTA 0x0000003E +#define ICHIP_HCR_ENABLE_INTB 0x003E0000 +#define ICHIP_HCR_IWDATA_FIFO 0x800000 + +/* (i)chip Hardware Status Register defines */ +#define ICHIP_HSR_INT_LATCH 0x02 + +/* (i)chip Hardware Address Mask Register defines */ +#define ICHIP_HAMR_BYTE_SWAP_ADDR_TR 0x08 +#define ICHIP_HAMR_BYTE_SWAP_NO_ADDR_TR 0x04 + +/* NOVRAM defines */ +#define IPH5526_NOVRAM_SIZE 64 + + +/* Offsets for the registers that correspond to the + * Qs on the Tachyon (As defined in the Tachyon Manual). + */ + +/* Outbound Command Queue (OCQ). + */ +#define OCQ_BASE_REGISTER_OFFSET 0x000 +#define OCQ_LENGTH_REGISTER_OFFSET 0x004 +#define OCQ_PRODUCER_REGISTER_OFFSET 0x008 +#define OCQ_CONSUMER_REGISTER_OFFSET 0x00C + +/* Inbound Message Queue (IMQ). + */ +#define IMQ_BASE_REGISTER_OFFSET 0x080 +#define IMQ_LENGTH_REGISTER_OFFSET 0x084 +#define IMQ_CONSUMER_REGISTER_OFFSET 0x088 +#define IMQ_PRODUCER_REGISTER_OFFSET 0x08C + +/* Multiframe Sequence Buffer Queue (MFSBQ) + */ +#define MFSBQ_BASE_REGISTER_OFFSET 0x0C0 +#define MFSBQ_LENGTH_REGISTER_OFFSET 0x0C4 +#define MFSBQ_PRODUCER_REGISTER_OFFSET 0x0C8 +#define MFSBQ_CONSUMER_REGISTER_OFFSET 0x0CC +#define MFS_LENGTH_REGISTER_OFFSET 0x0D0 + +/* Single Frame Sequence Buffer Queue (SFSBQ) + */ +#define SFSBQ_BASE_REGISTER_OFFSET 0x100 +#define SFSBQ_LENGTH_REGISTER_OFFSET 0x104 +#define SFSBQ_PRODUCER_REGISTER_OFFSET 0x108 +#define SFSBQ_CONSUMER_REGISTER_OFFSET 0x10C +#define SFS_LENGTH_REGISTER_OFFSET 0x110 + +/* SCSI Exchange State Table (SEST) + */ +#define SEST_BASE_REGISTER_OFFSET 0x140 +#define SEST_LENGTH_REGISTER_OFFSET 0x144 +#define SCSI_LENGTH_REGISTER_OFFSET 0x148 + +/* Length of the various Qs + */ +#define NO_OF_ENTRIES 8 +#define OCQ_LENGTH (MY_PAGE_SIZE/32) +#define IMQ_LENGTH (MY_PAGE_SIZE/32) +#define MFSBQ_LENGTH 8 +#define SFSBQ_LENGTH 8 +#define SEST_LENGTH MY_PAGE_SIZE + +/* Size of the various buffers. + */ +#define FRAME_SIZE 2048 +#define MFS_BUFFER_SIZE FRAME_SIZE +#define SFS_BUFFER_SIZE (FRAME_SIZE + TACHYON_HEADER_LEN) +#define SEST_BUFFER_SIZE 512 +#define TACH_HEADER_SIZE 64 +#define NO_OF_TACH_HEADERS ((MY_PAGE_SIZE)/TACH_HEADER_SIZE) + +#define NO_OF_FCP_CMNDS (MY_PAGE_SIZE/32) +#define SDB_SIZE 2048 +#define NO_OF_SDB_ENTRIES ((32*MY_PAGE_SIZE)/SDB_SIZE) + + +/* Offsets to the other Tachyon registers. + * (As defined in the Tachyon manual) + */ +#define TACHYON_CONFIG_REGISTER_OFFSET 0x184 +#define TACHYON_CONTROL_REGISTER_OFFSET 0x188 +#define TACHYON_STATUS_REGISTER_OFFSET 0x18C +#define TACHYON_FLUSH_SEST_REGISTER_OFFSET 0x190 + +/* Defines for the Tachyon Configuration register. + */ +#define SCSI_ENABLE 0x40000000 +#define WRITE_STREAM_SIZE 0x800 /* size = 16 */ +#define READ_STREAM_SIZE 0x300 /* size = 64 */ +#define PARITY_EVEN 0x2 +#define OOO_REASSEMBLY_DISABLE 0x40 + +/* Defines for the Tachyon Control register. + */ +#define SOFTWARE_RESET 0x80000000 +#define OCQ_RESET 0x4 +#define ERROR_RELEASE 0x2 + +/* Defines for the Tachyon Status register. + */ +#define RECEIVE_FIFO_EMPTY 0x10 +#define OSM_FROZEN 0x1 +#define OCQ_RESET_STATUS 0x20 +#define SCSI_FREEZE_STATUS 0x40 + + +/* Offsets to the Frame Manager registers. + */ +#define FMGR_CONFIG_REGISTER_OFFSET 0x1C0 +#define FMGR_CONTROL_REGISTER_OFFSET 0x1C4 +#define FMGR_STATUS_REGISTER_OFFSET 0x1C8 +#define FMGR_TIMER_REGISTER_OFFSET 0x1CC +#define FMGR_WWN_HI_REGISTER_OFFSET 0x1E0 +#define FMGR_WWN_LO_REGISTER_OFFSET 0x1E4 +#define FMGR_RCVD_ALPA_REGISTER_OFFSET 0x1E8 + +/* Defines for the Frame Manager Configuration register. + */ +#define BB_CREDIT 0x10000 +#define NPORT 0x8000 +#define LOOP_INIT_FABRIC_ADDRESS 0x400 +#define LOOP_INIT_PREVIOUS_ADDRESS 0x200 +#define LOOP_INIT_SOFT_ADDRESS 0x80 + +/* Defines for the Frame Manager Control register. + */ +#define HOST_CONTROL 0x02 +#define EXIT_HOST_CONTROL 0x03 +#define OFFLINE 0x05 +#define INITIALIZE 0x06 +#define CLEAR_LF 0x07 + +/* Defines for the Frame Manager Status register. + */ +#define LOOP_UP 0x80000000 +#define TRANSMIT_PARITY_ERROR 0x40000000 +#define NON_PARTICIPATING 0x20000000 +#define OUT_OF_SYNC 0x02000000 +#define LOSS_OF_SIGNAL 0x01000000 +#define NOS_OLS_RECEIVED 0x00080000 +#define LOOP_STATE_TIMEOUT 0x00040000 +#define LIPF_RECEIVED 0x00020000 +#define BAD_ALPA 0x00010000 +#define LINK_FAILURE 0x00001000 +#define ELASTIC_STORE_ERROR 0x00000400 +#define LINK_UP 0x00000200 +#define LINK_DOWN 0x00000100 +#define ARBITRATING 0x00000010 +#define ARB_WON 0x00000020 +#define OPEN 0x00000030 +#define OPENED 0x00000040 +#define TX_CLS 0x00000050 +#define RX_CLS 0x00000060 +#define TRANSFER 0x00000070 +#define INITIALIZING 0x00000080 +#define LOOP_FAIL 0x000000D0 +#define OLD_PORT 0x000000F0 +#define PORT_STATE_ACTIVE 0x0000000F +#define PORT_STATE_OFFLINE 0x00000000 +#define PORT_STATE_LF1 0x00000009 +#define PORT_STATE_LF2 0x0000000A + +/* Completion Message Types + * (defined in P.177 of the Tachyon manual) + */ +#define OUTBOUND_COMPLETION 0x000 +#define OUTBOUND_COMPLETION_I 0x100 +#define OUT_HI_PRI_COMPLETION 0x001 +#define OUT_HI_PRI_COMPLETION_I 0x101 +#define INBOUND_MFS_COMPLETION 0x102 +#define INBOUND_OOO_COMPLETION 0x003 +#define INBOUND_SFS_COMPLETION 0x104 +#define INBOUND_C1_TIMEOUT 0x105 +#define INBOUND_UNKNOWN_FRAME_I 0x106 +#define INBOUND_BUSIED_FRAME 0x006 +#define SFS_BUF_WARN 0x107 +#define MFS_BUF_WARN 0x108 +#define IMQ_BUF_WARN 0x109 +#define FRAME_MGR_INTERRUPT 0x10A +#define READ_STATUS 0x10B +#define INBOUND_SCSI_DATA_COMPLETION 0x10C +#define INBOUND_SCSI_COMMAND 0x10D +#define BAD_SCSI_FRAME 0x10E +#define INB_SCSI_STATUS_COMPLETION 0x10F + +/* One of the things that we care about when we receive an + * Outbound Completion Message (OCM). + */ +#define OCM_TIMEOUT_OR_BAD_ALPA 0x0800 + +/* Defines for the Tachyon Header structure. + */ +#define SOFI3 0x70 +#define SOFN3 0xB0 +#define EOFN 0x5 + +/* R_CTL */ +#define FC4_DEVICE_DATA 0 +#define EXTENDED_LINK_DATA 0x20000000 +#define FC4_LINK_DATA 0x30000000 +#define BASIC_LINK_DATA 0x80000000 +#define LINK_CONTROL 0xC0000000 +#define SOLICITED_DATA 0x1000000 +#define UNSOLICITED_CONTROL 0x2000000 +#define SOLICITED_CONTROL 0x3000000 +#define UNSOLICITED_DATA 0x4000000 +#define DATA_DESCRIPTOR 0x5000000 +#define UNSOLICITED_COMMAND 0x6000000 + +#define RCTL_ELS_UCTL 0x22000000 +#define RCTL_ELS_SCTL 0x23000000 +#define RCTL_BASIC_ABTS 0x81000000 +#define RCTL_BASIC_ACC 0x84000000 +#define RCTL_BASIC_RJT 0x85000000 + +/* TYPE */ +#define TYPE_BLS 0x00000000 +#define TYPE_ELS 0x01000000 +#define TYPE_FC_SERVICES 0x20000000 +#define TYPE_LLC_SNAP 0x05000000 +#define TYPE_FCP 0x08000000 + +/* F_CTL */ +#define EXCHANGE_RESPONDER 0x800000 +#define SEQUENCE_RESPONDER 0x400000 +#define FIRST_SEQUENCE 0x200000 +#define LAST_SEQUENCE 0x100000 +#define SEQUENCE_INITIATIVE 0x10000 +#define RELATIVE_OFF_PRESENT 0x8 +#define END_SEQUENCE 0x80000 + +#define TACHYON_HEADER_LEN 32 +#define NW_HEADER_LEN 16 +/* Defines for the Outbound Descriptor Block (ODB). + */ +#define ODB_CLASS_3 0xC000 +#define ODB_NO_COMP 0x400 +#define ODB_NO_INT 0x200 +#define ODB_EE_CREDIT 0xF + +/* Defines for the Extended Descriptor Block (EDB). + */ +#define EDB_LEN ((32*MY_PAGE_SIZE)/8) +#define EDB_END 0x8000 +#define EDB_FREE 0 +#define EDB_BUSY 1 + +/* Command Codes */ +#define ELS_LS_RJT 0x01000000 +#define ELS_ACC 0x02000000 +#define ELS_PLOGI 0x03000000 +#define ELS_FLOGI 0x04000000 +#define ELS_LOGO 0x05000000 +#define ELS_TPRLO 0x24000000 +#define ELS_ADISC 0x52000000 +#define ELS_PDISC 0x50000000 +#define ELS_PRLI 0x20000000 +#define ELS_PRLO 0x21000000 +#define ELS_SCR 0x62000000 +#define ELS_RSCN 0x61000000 +#define ELS_FARP_REQ 0x54000000 +#define ELS_ABTX 0x06000000 +#define ELS_ADVC 0x0D000000 +#define ELS_ECHO 0x10000000 +#define ELS_ESTC 0x0C000000 +#define ELS_ESTS 0x0B000000 +#define ELS_RCS 0x07000000 +#define ELS_RES 0x08000000 +#define ELS_RLS 0x0F000000 +#define ELS_RRQ 0x12000000 +#define ELS_RSS 0x09000000 +#define ELS_RTV 0x0E000000 +#define ELS_RSI 0x0A000000 +#define ELS_TEST 0x11000000 +#define ELS_RNC 0x53000000 +#define ELS_RVCS 0x41000000 +#define ELS_TPLS 0x23000000 +#define ELS_GAID 0x30000000 +#define ELS_FACT 0x31000000 +#define ELS_FAN 0x60000000 +#define ELS_FDACT 0x32000000 +#define ELS_NACT 0x33000000 +#define ELS_NDACT 0x34000000 +#define ELS_QoSR 0x40000000 +#define ELS_FDISC 0x51000000 + +#define ELS_NS_PLOGI 0x03FFFFFC + +/* LS_RJT reason codes. + */ +#define INV_LS_CMND_CODE 0x0001 +#define LOGICAL_ERR 0x0003 +#define LOGICAL_BUSY 0x0005 +#define PROTOCOL_ERR 0x0007 +#define UNABLE_TO_PERFORM 0x0009 +#define CMND_NOT_SUPP 0x000B + +/* LS_RJT explanation codes. + */ +#define NO_EXPLN 0x0000 +#define RECV_FIELD_SIZE 0x0700 +#define CONC_SEQ 0x0900 +#define REQ_NOT_SUPPORTED 0x2C00 +#define INV_PAYLOAD_LEN 0x2D00 + +/* Payload Length defines. + */ +#define PLOGI_LEN 116 + +#define CONCURRENT_SEQUENCES 0x01 +#define RO_INFO_CATEGORY 0xFE +#define E_D_TOV 0x07D0 /* 2 Secs */ +#define AL_TIME 0x0010 /* ~15 msec */ +#define TOV_VALUES (AL_TIME << 16) | E_D_TOV +#define RT_TOV 0x64 /* 100 msec */ +#define PTP_TOV_VALUES (RT_TOV << 16) | E_D_TOV +#define SERVICE_VALID 0x8000 +#define SEQUENCE_DELIVERY 0x0800 +#define CLASS3_CONCURRENT_SEQUENCE 0x01 +#define CLASS3_OPEN_SEQUENCE 0x01 + +/* These are retrieved from the NOVRAM. + */ +#define WORLD_WIDE_NAME_LOW fi->g.my_port_name_low +#define WORLD_WIDE_NAME_HIGH fi->g.my_port_name_high +#define N_PORT_NAME_HIGH fi->g.my_port_name_high +#define N_PORT_NAME_LOW fi->g.my_port_name_low +#define NODE_NAME_HIGH fi->g.my_node_name_high +#define NODE_NAME_LOW fi->g.my_node_name_low + +#define PORT_NAME_LEN 8 +#define NODE_NAME_LEN 8 + + +#define PH_VERSION 0x0909 + +#define LOOP_BB_CREDIT 0x00 +#define PT2PT_BB_CREDIT 0x01 +#define FLOGI_C_F 0x0800 /* Alternate BB_Credit Mgmnt */ +#define PLOGI_C_F 0x8800 /* Continuously Increasing + Alternate BB_Credit Management */ + +/* Fabric defines */ +#define DIRECTORY_SERVER 0xFFFFFC +#define FABRIC_CONTROLLER 0xFFFFFD +#define F_PORT 0xFFFFFE + +#define FLOGI_DID 0xFFFE +#define NS_PLOGI_DID 0xFFFC + +/* Fibre Channel Services defines */ +#define FCS_RFC_4 0x02170000 +#define FCS_GP_ID4 0x01A10000 +#define FCS_ACC 0x8002 +#define FCS_REJECT 0x8001 + +/* CT Header defines */ +#define FC_CT_REV 0x01000000 +#define DIRECTORY_SERVER_APP 0xFC +#define NAME_SERVICE 0x02 + +/* Port Type defines */ +#define PORT_TYPE_IP 0x05000000 +#define PORT_TYPE_NX_PORTS 0x7F000000 + +/* SCR defines */ +#define FABRIC_DETECTED_REG 0x00000001 +#define N_PORT_DETECTED_REG 0x00000002 +#define FULL_REGISTRATION 0x00000003 +#define CLEAR_REGISTRATION 0x000000FF + +/* Command structure has only one byte to address targets + */ +#define MAX_SCSI_TARGETS 0xFF + +#define FC_SCSI_READ 0x80 +#define FC_SCSI_WRITE 0x81 +#define FC_ELS 0x01 +#define FC_BLS 0x00 +#define FC_IP 0x05 +#define FC_BROADCAST 0xFF + +/* SEST defines. + */ +#define SEST_V 0x80000000 /* V = 1 */ +#define INB_SEST_VED 0xA0000000 /* V = 1, D = 1 */ +#define SEST_INV 0x7FFFFFFF +#define OUTB_SEST_VED 0x80000000 /* V = 1 */ +#define INV_SEQ_LEN 0xFFFFFFFF +#define OUTB_SEST_LINK 0xFFFF + +/* PRLI defines. + */ +#define PAGE_LEN 0x100000 /* 3rd byte - 0x10 */ +#define PRLI_LEN 0x0014 /* 20 bytes */ +#define FCP_TYPE_CODE 0x0800 /* FCP-SCSI */ +#define IMAGE_PAIR 0x2000 /* establish image pair */ +#define INITIATOR_FUNC 0x00000020 +#define TARGET_FUNC 0x00000010 +#define READ_XFER_RDY_DISABLED 0x00000002 + +#define NODE_PROCESS_LOGGED_IN 0x3 +#define NODE_NOT_PRESENT 0x2 +#define NODE_LOGGED_IN 0x1 +#define NODE_LOGGED_OUT 0x0 + +/* Defines to determine what should be returned when a SCSI frame + * times out. + */ +#define FC_SCSI_BAD_TARGET 0xFFFE0000 + +/* RSCN Address formats */ +#define PORT_ADDRESS_FORMAT 0x00 +#define AREA_ADDRESS_FORMAT 0x01 +#define DOMAIN_ADDRESS_FORMAT 0x02 + +/* Defines used to determine whether a frame transmission should + * be indicated by an interrupt or not. + */ +#define NO_COMP_AND_INT 0 +#define INT_AND_COMP_REQ 1 +#define NO_INT_COMP_REQ 2 + +/* Other junk... + */ +#define SDB_FREE 0 +#define SDB_BUSY 1 +#define MAX_PENDING_FRAMES 15 +#define RX_ID_FIRST_SEQUENCE 0xFFFF +#define OX_ID_FIRST_SEQUENCE 0xFFFF +#define NOT_SCSI_XID 0x8000 +#define MAX_SCSI_XID 0x0FFF /* X_IDs are from 0-4095 */ +#define SCSI_READ_BIT 0x4000 +#define MAX_SCSI_OXID 0x4FFF +#define OXID_AVAILABLE 0 +#define OXID_INUSE 1 +#define MAX_SEQ_ID 0xFF + +#define INITIATOR 2 +#define TARGET 1 +#define DELETE_ENTRY 1 +#define ADD_ENTRY 2 + +#endif /* _TACH_H */ diff -u --recursive --new-file v2.2.11/linux/drivers/net/fc/tach_structs.h linux/drivers/net/fc/tach_structs.h --- v2.2.11/linux/drivers/net/fc/tach_structs.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/fc/tach_structs.h Wed Aug 25 17:29:48 1999 @@ -0,0 +1,428 @@ +/********************************************************************** + * iph5526.c: Structures for the Interphase 5526 PCI Fibre Channel + * IP/SCSI driver. + * Copyright (C) 1999 Vineet M Abraham + **********************************************************************/ + +#ifndef _TACH_STRUCT_H +#define _TACH_STRUCT_H + +typedef struct { + u_short cmnd_code; + u_short payload_length; + u_short type_code; + u_short est_image_pair; + u_int originator_pa; + u_int responder_pa; + u_int service_params; +} PRLI; + +typedef struct { + u_int flags_and_byte_offset; + u_int byte_count; + u_short no_of_recvd_frames; + u_short no_of_expected_frames; + u_int last_fctl; + u_int sdb_address; + u_int scratch_pad; + u_int expected_ro; + u_short buffer_index; + u_short buffer_offset; + } INB_SEST_ENTRY; + +typedef struct { + u_int flags_and_did; + u_short max_frame_len; + u_short cntl; + u_int total_seq_length; + u_short link; + u_short rx_id; + u_int transaction_id; + u_int header_address; + u_char seq_id; + u_char reserved; + u_short header_length; + u_int edb_address; + } OUTB_SEST_ENTRY; + +typedef struct { + u_short d_naa; + u_short dest_high; + u_int dest_low; + u_short s_naa; + u_short source_high; + u_int source_low; + } NW_HEADER; + +typedef struct { + u_int resv; + u_char sof_and_eof; + u_char dest_alpa; + u_short lcr_and_time_stamp; + u_int r_ctl_and_d_id; + u_int vc_id_and_s_id; + u_int type_and_f_cntl; + u_char seq_id; + u_char df_cntl; + u_short seq_cnt; + u_short ox_id; + u_short rx_id; + u_int ro; + NW_HEADER nw_header; + } TACHYON_HEADER; + +typedef struct { + u_short service_options; + u_short initiator_ctl; + u_short recipient_ctl; + u_short recv_data_field_size; + u_short concurrent_sequences; + u_short n_port_end_to_end_credit; + u_short open_seq_per_exchange; + u_short resv; + }CLASS_OF_SERVICE; + +typedef struct { + u_int logo_cmnd; + u_char reserved; + u_char n_port_id_2; + u_char n_port_id_1; + u_char n_port_id_0; + u_int port_name_up; + u_int port_name_low; + } LOGO; + +typedef struct { + u_int ls_cmnd_code; + u_int hard_address; + u_int port_name_high; + u_int port_name_low; + u_int node_name_high; + u_int node_name_low; + u_int n_port_id; + } ADISC; + +typedef struct { + u_int cmnd_code; + u_int reason_code; + } LS_RJT; + +typedef struct { + u_int cmnd_code; + } ACC; + +typedef struct { + u_int seq_d_id; + u_int tot_len; + u_short cntl; + u_short rx_id; + u_short cs_enable; + u_short cs_seed; + u_int trans_id; + u_int hdr_addr; + u_short frame_len; + u_short hdr_len; + u_int edb_addr; + }ODB; + +typedef struct { + u_int cmnd_code; + u_int reg_function; /* in the last byte */ + } SCR; + +typedef struct { + u_int rev_in_id; + u_char fs_type; + u_char fs_subtype; + u_char options; + u_char resv1; + u_short cmnd_resp_code; + u_short max_res_size; + u_char resv2; + u_char reason_code; + u_char expln_code; + u_char vendor_unique; + } CT_HDR; + +typedef struct { + CT_HDR ct_hdr; + u_int s_id; + u_char bit_map[32]; /* 32 byte bit map */ + } RFC_4; + +typedef struct { + u_int ls_cmnd_code; + u_short fc_ph_version; + u_short buff_to_buff_credit; + u_short common_features; + u_short recv_data_field_size; + u_short n_port_total_conc_seq; + u_short rel_off_by_info_cat; + u_int ED_TOV; + u_int n_port_name_high; + u_int n_port_name_low; + u_int node_name_high; + u_int node_name_low; + CLASS_OF_SERVICE c_of_s[3]; + u_int resv[4]; + u_int vendor_version_level[4]; + }LOGIN; + +typedef struct { + CT_HDR ct_hdr; + u_int port_type; /* in the first byte */ + } GP_ID4; + +typedef struct { + u_int buf_addr; + u_short ehf; + u_short buf_len; + }EDB; + +/* (i)chip Registers */ +struct i_chip_regs { + u_int ptr_ichip_hw_control_reg; + u_int ptr_ichip_hw_status_reg; + u_int ptr_ichip_hw_addr_mask_reg; +}; + +struct iph5526_novram { + u_int ptr_novram_hw_control_reg; + u_int ptr_novram_hw_status_reg; + u_short data[IPH5526_NOVRAM_SIZE]; +}; + +/* Tachyon Registers */ +struct tachyon_regs { + u_int ptr_ocq_base_reg; + u_int ptr_ocq_len_reg; + u_int ptr_ocq_prod_indx_reg; + u_int ptr_ocq_cons_indx_reg; + + u_int ptr_imq_base_reg; + u_int ptr_imq_len_reg; + u_int ptr_imq_cons_indx_reg; + u_int ptr_imq_prod_indx_reg; + + u_int ptr_mfsbq_base_reg; + u_int ptr_mfsbq_len_reg; + u_int ptr_mfsbq_prod_reg; + u_int ptr_mfsbq_cons_reg; + u_int ptr_mfsbuff_len_reg; + + u_int ptr_sfsbq_base_reg; + u_int ptr_sfsbq_len_reg; + u_int ptr_sfsbq_prod_reg; + u_int ptr_sfsbq_cons_reg; + u_int ptr_sfsbuff_len_reg; + + u_int ptr_sest_base_reg; + u_int ptr_sest_len_reg; + u_int ptr_scsibuff_len_reg; + + u_int ptr_tach_config_reg; + u_int ptr_tach_control_reg; + u_int ptr_tach_status_reg; + u_int ptr_tach_flush_oxid_reg; + + u_int ptr_fm_config_reg; + u_int ptr_fm_control_reg; + u_int ptr_fm_status_reg; + u_int ptr_fm_tov_reg; + u_int ptr_fm_wwn_hi_reg; + u_int ptr_fm_wwn_low_reg; + u_int ptr_fm_rx_al_pa_reg; +}; + +struct globals { + u_long tachyon_base; + u_int *mem_base; + u_short ox_id; /* OX_ID used for IP and ELS frames */ + u_short scsi_oxid; /* OX_ID for SEST entry */ + u_char seq_id; + u_int my_id; + u_int my_ddaa; /* my domain and area in a fabric */ + volatile u_char loop_up; + volatile u_char ptp_up; /* we have a point-to-point link */ + volatile u_char link_up; + volatile u_char n_port_try; + volatile u_char nport_timer_set; + volatile u_char lport_timer_set; + /* Hmmm... We dont want to Initialize while closing */ + u_char dont_init; + u_int my_node_name_high; + u_int my_node_name_low; + u_int my_port_name_high; + u_int my_port_name_low; + u_char fabric_present; + u_char explore_fabric; + u_char name_server; + u_int my_mtu; + u_int *els_buffer[MAX_PENDING_FRAMES]; /* temp space for ELS frames */ + char *arp_buffer; /* temp space for ARP frames */ + u_int mfs_buffer_count; /* keep track of MFS buffers used*/ + u_char scsi_registered; + /* variables for port discovery */ + volatile u_char port_discovery; + volatile u_char perform_adisc; + u_short alpa_list_index; + u_short type_of_frame; /* Could be IP/SCSI Read/SCSI Write*/ + u_char no_of_targets; /* used to assign target_ids */ + u_long sem; /* to synchronize between IP and SCSI */ + u_char e_i; + + /* the frames */ + TACHYON_HEADER tach_header; + LOGIN login; + PRLI prli; + LOGO logo; + ADISC adisc; + LS_RJT ls_rjt; + ODB odb; + INB_SEST_ENTRY inb_sest_entry; + OUTB_SEST_ENTRY outb_sest_entry; + ACC acc; + SCR scr; + EDB edb; + RFC_4 rfc_4; + GP_ID4 gp_id4; +}; + +struct queue_variables { + /* Indices maintained in host memory. + */ + u_int *host_ocq_cons_indx, *host_hpcq_cons_indx, *host_imq_prod_indx; + u_int *ptr_host_ocq_cons_indx, *ptr_host_hpcq_cons_indx, *ptr_host_imq_prod_indx; + + /* Variables for Outbound Command Queue (OCQ). + */ + u_int *ptr_ocq_base; + u_int ocq_len, ocq_end; + u_int ocq_prod_indx; + u_int *ptr_odb[OCQ_LENGTH]; + + /* Variables for Inbound Message Queue (IMQ). + */ + u_int *ptr_imq_base; + u_int imq_len, imq_end; + u_int imq_cons_indx; + u_int imq_prod_indx; + u_int *ptr_imqe[IMQ_LENGTH]; + + u_int *ptr_mfsbq_base; + u_int mfsbq_len, mfsbq_end; + u_int mfsbq_prod_indx; + u_int mfsbq_cons_indx; + u_int mfsbuff_len, mfsbuff_end; + + u_int *ptr_sfsbq_base; + u_int sfsbq_len, sfsbq_end; + u_int sfsbq_prod_indx; + u_int sfsbq_cons_indx; + u_int sfsbuff_len, sfsbuff_end; + u_int *ptr_sfs_buffers[SFSBQ_LENGTH * NO_OF_ENTRIES]; + + /* Tables for SCSI Transactions */ + u_int *ptr_sest_base; + u_int *ptr_sest[SEST_LENGTH]; + u_char free_scsi_oxid[SEST_LENGTH]; + u_int *ptr_sdb_base; + u_int *ptr_sdb_slot[NO_OF_SDB_ENTRIES]; + u_char sdb_slot_status[NO_OF_SDB_ENTRIES]; + u_int sdb_indx; + u_int *ptr_fcp_cmnd_base; + u_int *ptr_fcp_cmnd[NO_OF_FCP_CMNDS]; + u_int fcp_cmnd_indx; + + /* Table for data to be transmitted. + */ + u_int *ptr_edb_base; + u_int *ptr_edb[EDB_LEN]; + u_int edb_buffer_indx; + volatile u_char free_edb_list[EDB_LEN]; + + /* Table of Tachyon Headers. + */ + u_int *ptr_tachyon_header[NO_OF_TACH_HEADERS]; + u_int *ptr_tachyon_header_base; + u_int tachyon_header_indx; +}; + +/* Used to match incoming ACCs to ELS requests sent out */ +struct ox_id_els_map { + u_short ox_id; + u_int els; + struct ox_id_els_map *next; +}; + + +/* Carries info about individual nodes... stores the info got at login + * time. Also maintains mapping between MAC->FC addresses + */ +struct fc_node_info { + /* Itz the WWN (8 bytes), the last 6 bytes is the MAC address */ + u_char hw_addr[PORT_NAME_LEN]; + u_char node_name[NODE_NAME_LEN]; + u_int d_id; /*real FC address, 3 bytes */ + int mtu; + /* login = 1 if login attempted + * login = 2 if login completed + */ + int login; + u_char scsi; /* = 1 if device is a SCSI Target */ + u_char target_id; + CLASS_OF_SERVICE c_of_s[3]; + struct fc_node_info *next; +}; + +struct fc_info { + char name[8]; + u_long base_addr; + int irq; + struct net_device_stats fc_stats; + struct fc_node_info *node_info_list; + int num_nodes; + struct ox_id_els_map *ox_id_list; + struct i_chip_regs i_r; + struct tachyon_regs t_r; + struct queue_variables q; + struct globals g; + struct iph5526_novram n_r; + u_short clone_id; + struct timer_list nport_timer; + struct timer_list lport_timer; + struct timer_list explore_timer; + struct timer_list display_cache_timer; + struct device *dev; + struct Scsi_Host *host; + spinlock_t fc_lock; +}; + +struct iph5526_hostdata { + struct fc_info *fi; + fcp_cmd cmnd; + Scsi_Cmnd *cmnd_handler[SEST_LENGTH]; + u_int tag_ages[MAX_SCSI_TARGETS]; +}; + +/* List of valid AL_PAs */ +u_char alpa_list[127] = { + 0x00, 0x01, 0x02, 0x04, 0x08, 0x0F, 0x10, 0x17, + 0x18, 0x1B, 0x1D, 0x1E, 0x1F, 0x23, 0x25, 0x26, + 0x27, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x39, 0x3A, 0x3C, + 0x43, 0x45, 0x46, 0x47, 0x49, 0x4A, 0x4B, 0x4C, + 0x4D, 0x4E, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, + 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x67, 0x69, + 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x79, 0x7A, 0x7C, 0x80, 0x81, + 0x82, 0x84, 0x88, 0x8F, 0x90, 0x97, 0x98, 0x9B, + 0x9D, 0x9E, 0x9F, 0xA3, 0xA5, 0xA6, 0xA7, 0xA9, + 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xB1, 0xB2, 0xB3, + 0xB4, 0xB5, 0xB6, 0xB9, 0xBA, 0xBC, 0xC3, 0xC5, + 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, + 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD9, 0xDA, + 0xDC, 0xE0, 0xE1, 0xE2, 0xE4, 0xE8, 0xEF +}; + +#endif /* _TACH_STRUCT_H */ diff -u --recursive --new-file v2.2.11/linux/drivers/net/net_init.c linux/drivers/net/net_init.c --- v2.2.11/linux/drivers/net/net_init.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/net_init.c Wed Aug 25 17:29:48 1999 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -645,7 +646,130 @@ } #endif - + +#ifdef CONFIG_NET_FC + +#define MAX_FC_CARDS 2 +static struct device *fcdev_index[MAX_FC_CARDS]; + +void fc_setup(struct device *dev) +{ +int i; + + /* register boot-defined "fc" devices */ + if (dev->name && (strncmp(dev->name, "fc", 2) == 0)) { + i = simple_strtoul(dev->name + 2, NULL, 0); + if (fcdev_index[i] == NULL) { + fcdev_index[i] = dev; + } + else if (dev != fcdev_index[i]) { + /* Really shouldn't happen! */ + printk("fc_setup: Ouch! Someone else took %s\n", + dev->name); + } + } + + dev->hard_header = fc_header; + dev->rebuild_header = fc_rebuild_header; + + dev->type = ARPHRD_IEEE802; + dev->hard_header_len = FC_HLEN; + dev->mtu = 2024; + dev->addr_len = FC_ALEN; + dev->tx_queue_len = 100; /* Long queues on fc */ + + memset(dev->broadcast,0xFF, FC_ALEN); + + /* New-style flags. */ + dev->flags = IFF_BROADCAST; + dev_init_buffers(dev); + return; +} + + +struct device *init_fcdev(struct device *dev, int sizeof_priv) +{ +int new_device = 0; +int i; + /* Use an existing correctly named device in Space.c:dev_base. */ + if (dev == NULL) { + int alloc_size = sizeof(struct device) + sizeof("fc%d ") + sizeof_priv + 3; + struct device *cur_dev; + char pname[8]; /* Putative name for the device. */ + + for (i = 0; i < MAX_FC_CARDS; ++i) + if (fcdev_index[i] == NULL) { + sprintf(pname, "fc%d", i); + for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) + if (strcmp(pname, cur_dev->name) == 0) { + dev = cur_dev; + dev->init = NULL; + sizeof_priv = (sizeof_priv + 3) &~3; + dev->priv = sizeof_priv + ? kmalloc(sizeof_priv, GFP_KERNEL) + : NULL; + if (dev->priv) memset(dev->priv, 0, sizeof_priv); + goto fcfound; + } + } + + alloc_size &= ~3; /* Round to dword boundary. */ + dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL); + memset(dev, 0, alloc_size); + if (sizeof_priv) + dev->priv = (void *) (dev + 1); + dev->name = sizeof_priv + (char *)(dev + 1); + new_device = 1; + } + +fcfound: /* From the double loop */ + + for (i = 0; i < MAX_FC_CARDS; ++i) + if (fcdev_index[i] == NULL) { + sprintf(dev->name, "fc%d", i); + fcdev_index[i] = dev; + break; + } + + fc_setup(dev); + if (new_device) + register_netdevice(dev); + + return dev; +} + +void fc_freedev(struct device *dev) +{ +int i; + for (i = 0; i < MAX_FC_CARDS; ++i) { + if (fcdev_index[i] == dev) { + fcdev_index[i] = NULL; + break; + } + } +} + + +int register_fcdev(struct device *dev) +{ + dev_init_buffers(dev); + if (dev->init && dev->init(dev) != 0) { + unregister_fcdev(dev); + return -EIO; + } + return 0; +} + +void unregister_fcdev(struct device *dev) +{ + rtnl_lock(); + unregister_netdevice(dev); + rtnl_unlock(); + fc_freedev(dev); +} + +#endif /* CONFIG_NET_FC */ + /* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c" diff -u --recursive --new-file v2.2.11/linux/drivers/net/rtl8139.c linux/drivers/net/rtl8139.c --- v2.2.11/linux/drivers/net/rtl8139.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/rtl8139.c Wed Aug 25 17:29:48 1999 @@ -1042,9 +1042,9 @@ rtl8129_rx(dev); if (status & (TxOK | TxErr)) { - unsigned int dirty_tx = tp->dirty_tx; + unsigned int dirty_tx; - while (tp->cur_tx - dirty_tx > 0) { + for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; dirty_tx++) { int entry = dirty_tx % NUM_TX_DESC; int txstatus = inl(ioaddr + TxStatus0 + entry*4); @@ -1091,7 +1091,6 @@ clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } - dirty_tx++; } #ifndef final_version diff -u --recursive --new-file v2.2.11/linux/drivers/net/sb1000.c linux/drivers/net/sb1000.c --- v2.2.11/linux/drivers/net/sb1000.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/sb1000.c Wed Aug 25 17:29:48 1999 @@ -273,7 +273,7 @@ dev->type = ARPHRD_ETHER; dev->hard_header_len = 0; - dev->mtu = 0; + dev->mtu = 1500; dev->addr_len = ETH_ALEN; /* hardware address is 0:0:serial_number */ dev->dev_addr[0] = 0; diff -u --recursive --new-file v2.2.11/linux/drivers/net/sis900.c linux/drivers/net/sis900.c --- v2.2.11/linux/drivers/net/sis900.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/net/sis900.c Wed Aug 25 17:29:48 1999 @@ -33,6 +33,8 @@ static int multicast_filter_limit = 128; #define MAX_UNITS 8 /* More are supported, limit only on options */ +static int speeds[MAX_UNITS] = {100, 100, 100, 100, 100, 100, 100, 100}; +static int full_duplex[MAX_UNITS] = {1, 1, 1, 1, 1, 1, 1, 1}; #define TX_BUF_SIZE 1536 #define RX_BUF_SIZE 1536 @@ -41,9 +43,9 @@ #define RX_DMA_BURST 0 #define TX_FIFO_THRESH 16 #define TxDRNT_100 (1536>>5) -#define TxDRNT_10 16 //(1536>>5) +#define TxDRNT_10 16 #define RxDRNT_100 8 -#define RxDRNT_10 8 //(1536>>5) +#define RxDRNT_10 8 #define TRUE 1 #define FALSE 0 @@ -387,21 +389,23 @@ EuphLiteDesc rx_buf[NUM_RX_DESC]; unsigned char *rx_bufs; unsigned char *tx_bufs; /* Tx bounce buffer region. */ - char phys[4]; /* MII device addresses. */ - int phy_idx; + char phys[4]; /* MII device addresses. */ + int phy_idx; /* Support Max 4 PHY */ u16 pmd_status; - unsigned int tx_full; /* The Tx queue is full. */ - u16 full_duplex; /* FullHalf-duplex. */ - u16 hunmbps; /* 10010 Mbps. */ + unsigned int tx_full; /* The Tx queue is full. */ + int MediaSpeed; /* user force speed */ + int MediaDuplex; /* user force duplex */ + int full_duplex; /* Full/Half-duplex. */ + int speeds; /* 100/10 Mbps. */ u16 LinkOn; u16 LinkChange; }; #ifdef MODULE #if LINUX_VERSION_CODE > 0x20115 -MODULE_AUTHOR("Silicon Integrated Systems Corporation"); +MODULE_AUTHOR("Jim Huang "); MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(speeds, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(multicast_filter_limit, "i"); MODULE_PARM(max_interrupt_work, "i"); @@ -425,6 +429,7 @@ static void set_rx_mode(struct device *dev); static void sis900_reset(struct device *dev); static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed); +static void elSetCapability(struct device *dev, int phy_id, int duplex, int speed); static u16 elPMDreadMode(struct device *dev, int phy_id, int *speed, int *duplex); static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, u16 polarity, u16 *value); static void elSetMediaType(struct device *dev, int speed, int duplex); @@ -544,14 +549,18 @@ { static int did_version = 0; /* Already printed version info. */ struct sis900_private *tp; - int duplex=0; - int speed=0; - int i; + u16 status; + int duplex = found_cnt < MAX_UNITS ? full_duplex[found_cnt] : 0 ; + int speed = found_cnt < MAX_UNITS ? speeds[found_cnt] : 0 ; + int phy=0, phy_idx=0, i; if (did_version++ == 0) printk(KERN_INFO "%s", version); dev = init_etherdev(dev, 0); + + if(dev==NULL) + return NULL; printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", dev->name, pci_tbl[chip_idx].name, ioaddr, irq); @@ -574,6 +583,11 @@ /* Some data structures must be quadword aligned. */ tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA); + if(tp==NULL) + { + releaseregion(ioaddr, pci_tbl[chip_idx].io_size); + return NULL; + } memset(tp, 0, sizeof(*tp)); dev->priv = tp; @@ -588,25 +602,11 @@ Doing this in open() would allow detecting external xcvrs later, but takes too much time. */ if (sis_cap_tbl[chip_idx] & HAS_MII_XCVR) { - int phy, phy_idx; for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(dev, phy, MII_STATUS); - /* - { - int p; - int l; - for (p = 0 ; p < 4 ; p ++) { - for (l=0 ; l<16 ; l++) { - int status = mdio_read(dev, phy, l); - status = mdio_read(dev, phy, l); - printk(KERN_INFO "MII info addr[%d][%d]=%x\n", - p, l, status); - } - } - } - */ + int mii_status ; + mii_status = mdio_read(dev, phy, MII_STATUS); if (mii_status != 0xffff && mii_status != 0x0000) { tp->phy_idx = phy_idx; @@ -620,45 +620,85 @@ } if (phy_idx == 0) { - printk(KERN_INFO "%s: No MII transceivers found!", + printk(KERN_INFO "%s: No MII transceivers found!\n", dev->name); - printk(KERN_INFO "Assuming SYM transceiver.\n"); tp->phys[0] = -1; + tp->pmd_status = 0; } } else { - tp->phys[0] = 32; + tp->phys[0] = -1; + tp->pmd_status = 0; } - if (tp->pmd_status > 0) { - tp->pmd_status= - elAutoNegotiate(dev, tp->phys[tp->phy_idx], &duplex, &speed); - if (tp->pmd_status & MIISTAT_LINK) { - if (duplex == FDX_CAPABLE_FULL_SELECTED) - tp->full_duplex=1; - if (speed == HW_SPEED_100_MBPS) - tp->hunmbps=1; - tp->LinkOn = TRUE; - } else { + if ((tp->pmd_status > 0) && (phy_idx > 0)) { + if (sis900_debug > 1) { + printk(KERN_INFO "duplex=%d, speed=%d\n", + duplex, speed); + } + if (!duplex && !speed) { + // auto-config media type + // Set full capability + if (sis900_debug > 1) { + printk(KERN_INFO "Auto Config ...\n"); + } + elSetCapability(dev, tp->phys[tp->phy_idx], 1, 100); + tp->pmd_status=elAutoNegotiate(dev, + tp->phys[tp->phy_idx], + &tp->full_duplex, + &tp->speeds); + } else { + tp->MediaSpeed = speed; + tp->MediaDuplex = duplex; + elSetCapability(dev, tp->phys[tp->phy_idx], + duplex, speed); + elAutoNegotiate(dev, tp->phys[tp->phy_idx], + &tp->full_duplex, + &tp->speeds); + status = mdio_read(dev, phy, MII_ANLPAR); + if ( !(status & (MII_NWAY_T | MII_NWAY_T_FDX | + MII_NWAY_TX | MII_NWAY_TX_FDX ))) + { + u16 cmd=0; + cmd |= ( speed == 100 ? + MIICNTL_SPEED : 0 ); + cmd |= ( duplex ? MIICNTL_FDX : 0 ); + mdio_write(dev, phy, MII_CONTROL, cmd); + elSetMediaType(dev, speed==100 ? + HW_SPEED_100_MBPS : + HW_SPEED_10_MBPS, + duplex ? + FDX_CAPABLE_FULL_SELECTED: + FDX_CAPABLE_HALF_SELECTED); + elMIIpollBit(dev, phy, MII_STATUS, + MIISTAT_LINK, TRUE, &status); + } else { + status = mdio_read(dev, phy, MII_STATUS); + } + } + + if (tp->pmd_status & MIISTAT_LINK) + tp->LinkOn = TRUE; + else tp->LinkOn = FALSE; - } - tp->LinkChange = FALSE; - } - - /* - if (found_cnt < MAX_UNITS && full_duplex[found_cnt] > 0) - tp->full_duplex = full_duplex[found_cnt]; - if (tp->full_duplex) { - printk(KERN_INFO "%s: Media type is Full Duplex.\n", dev->name); - } else { - printk(KERN_INFO "%s: Media type is Half Duplex.\n", dev->name); - } - if (tp->hunmbps) { - printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name); - } else { - printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name); + tp->LinkChange = FALSE; + } - */ + + if (sis900_debug > 1) { + if (tp->full_duplex == FDX_CAPABLE_FULL_SELECTED) { + printk(KERN_INFO "%s: Media type is Full Duplex.\n", + dev->name); + } else { + printk(KERN_INFO "%s: Media type is Half Duplex.\n", + dev->name); + } + if (tp->speeds == HW_SPEED_100_MBPS) { + printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name); + } else { + printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name); + } + } /* The SiS900-specific entries in the device structure. */ dev->open = &sis900_open; @@ -857,16 +897,23 @@ return -EAGAIN; } - MOD_INC_USE_COUNT; + MOD_INC_USE_COUNT; tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL); tp->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL); if (tp->tx_bufs == NULL || tp->rx_bufs == NULL) { if (tp->tx_bufs) kfree(tp->tx_bufs); - if (sis900_debug > 0) - printk(KERN_ERR "%s: Couldn't allocate a %d byte receive ring.\n", + if (tp->rx_bufs) + kfree(tp->rx_bufs); + if (!tp->tx_bufs) { + printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n", dev->name, TX_BUF_SIZE * NUM_TX_DESC); + } + if (!tp->rx_bufs) { + printk(KERN_ERR "%s: Can't allocate a %d byte RX Bufs.\n", + dev->name, RX_BUF_SIZE * NUM_RX_DESC); + } return -ENOMEM; } @@ -886,15 +933,6 @@ i, inl(ioaddr + rfdr)); } } - /* - for (i=0 ; i<3 ; i++) { - outl((((u32) i) << RFEP_shift), ioaddr + rfcr); - if (sis900_debug > 4) { - printk(KERN_INFO "Read Filter Addr[%d]=%x\n", - i, inl(ioaddr + rfdr)); - } - } - */ outl(rfcrSave, rfcr); } @@ -919,31 +957,28 @@ /* Must enable Tx/Rx before setting transfer thresholds! */ /* - #define TX_DMA_BURST 0 - #define RX_DMA_BURST 0 - #define TX_FIFO_THRESH 16 - #define TxDRNT_100 (1536>>5) - #define TxDRNT_10 (1536>>5) - #define RxDRNT_100 (1536>>5) - #define RxDRNT_10 (1536>>5) - */ - //outl(RxENA | TxENA, ioaddr + cr); + * #define TX_DMA_BURST 0 + * #define RX_DMA_BURST 0 + * #define TX_FIFO_THRESH 16 + * #define TxDRNT_100 (1536>>5) + * #define TxDRNT_10 (1536>>5) + * #define RxDRNT_100 (1536>>5) + * #define RxDRNT_10 (1536>>5) + */ outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg); outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10, ioaddr + txcfg); - //tp->full_duplex = tp->duplex_lock; - if (tp->phys[tp->phy_idx] >= 0 || - (sis_cap_tbl[tp->chip_id] & HAS_MII_XCVR)) { - if (sis900_debug > 1) - if (tp->LinkOn) { - printk(KERN_INFO"%s: Setting %s%s-duplex.\n", + if (sis900_debug > 1) + if (tp->LinkOn) { + printk(KERN_INFO"%s: Media Type %s%s-duplex.\n", dev->name, - tp->hunmbps ? "100mbps " : "10mbps ", - tp->full_duplex ? "full" : "half"); - } else { - printk(KERN_INFO"%s: Media Link Off\n", dev->name); - } - } + tp->speeds==HW_SPEED_100_MBPS ? + "100mbps " : "10mbps ", + tp->full_duplex== FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + } else { + printk(KERN_INFO"%s: Media Link Off\n", dev->name); + } set_rx_mode(dev); dev->tbusy = 0; @@ -955,7 +990,7 @@ outl(RxENA, ioaddr + cr); outl(IE, ioaddr + ier); - if (sis900_debug > 1) + if (sis900_debug > 3) printk(KERN_INFO "%s: sis900_open() ioaddr %#lx IRQ %d \n", dev->name, ioaddr, dev->irq); @@ -974,81 +1009,31 @@ { struct device *dev = (struct device *)data; struct sis900_private *tp = (struct sis900_private *)dev->priv; - long ioaddr = dev->base_addr; int next_tick = 0; - int duplex, full_duplex=0; - int speed, hunmbps=0; u16 status; -// printk(KERN_INFO "%s: SiS900 timer\n", dev->name); - elMIIpollBit(dev, tp->phys[tp->phy_idx], MII_STATUS, MIISTAT_LINK, TRUE, &status); - if (status & MIISTAT_LINK) { -// printk(KERN_INFO "%s: SiS900 timer link\n", dev->name); - /* - if (!tp->LinkOn) { - printk(KERN_INFO "%s: AutoNegotiate ...\n", dev->name); - tp->LinkChange=TRUE; - tp->pmd_status= - elAutoNegotiate(dev, tp->phys[tp->phy_idx], - &duplex, &speed); - } - else { - printk(KERN_INFO "%s: Link Still On.\n", dev->name); - elPMDreadMode(dev, tp->phys[tp->phy_idx], - &speed, &duplex); - } - */ - elPMDreadMode(dev, tp->phys[tp->phy_idx], - &speed, &duplex); - - - if (duplex == FDX_CAPABLE_FULL_SELECTED) full_duplex=1; - if (speed == HW_SPEED_100_MBPS) hunmbps=1; - if (full_duplex != tp->full_duplex || hunmbps != tp->hunmbps) - tp->LinkChange = TRUE; - if (tp->LinkChange) { - tp->full_duplex=full_duplex; - tp->hunmbps=hunmbps; - //elSetMediaType(dev, speed, duplex); - printk(KERN_INFO "%s: Setting %s%s-duplex based on MII " - "#%d link partner ability.\n", + if (!tp->LinkOn) { + status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); + if (status & MIISTAT_LINK) { + elPMDreadMode(dev, tp->phys[tp->phy_idx], + &tp->speeds, &tp->full_duplex); + tp->LinkOn = TRUE; + printk(KERN_INFO "%s: Media Link On %s%s-duplex ", dev->name, - tp->hunmbps ? "100mbps " : "10mbps ", - tp->full_duplex ? "full" : "half", - tp->phys[0]); - } - tp->LinkOn=TRUE; - tp->LinkChange = FALSE; - } else { - if (tp->LinkOn) { - tp->LinkChange = TRUE; - tp->pmd_status= - elAutoNegotiate(dev, tp->phys[tp->phy_idx], - &duplex, &speed); - if (tp->pmd_status & MIISTAT_LINK) { - if (duplex == FDX_CAPABLE_FULL_SELECTED) - tp->full_duplex=1; - if (speed == HW_SPEED_100_MBPS) - tp->hunmbps=1; - } else { - tp->LinkOn = FALSE; - printk(KERN_INFO "%s: Link Off\n", dev->name); - } - } -// printk(KERN_INFO "%s: Link Off\n", dev->name); + tp->speeds == HW_SPEED_100_MBPS ? + "100mbps " : "10mbps ", + tp->full_duplex==FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + } + } else { // previous link on + status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); + if (!(status & MIISTAT_LINK)) { + tp->LinkOn = FALSE; + printk(KERN_INFO "%s: Media Link Off\n", dev->name); + } } next_tick = 2*HZ; - if (sis900_debug > 3) { - printk(KERN_INFO "%s: Other registers are IntMask " - "%4.4x IntStatus %4.4x" - " RxStatus %4.4x.\n", - dev->name, - inw(ioaddr + imr), - inw(ioaddr + isr), - inl(ioaddr + rxcfg)); - } - if (next_tick) { tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer); @@ -1067,24 +1052,28 @@ /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); + /* Emit info to figure out what went wrong. */ - printk(KERN_INFO "%s: Tx queue start entry %d dirty entry %d.\n", - dev->name, tp->cur_tx, tp->dirty_tx); - for (i = 0; i < NUM_TX_DESC; i++) - printk(KERN_INFO "%s: Tx descriptor %d is %8.8x.%s\n", - dev->name, i, (unsigned int)&tp->tx_buf[i], - i == tp->dirty_tx % NUM_TX_DESC ? - " (queue head)" : ""); - /* - printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); - for (mii_reg = 0; mii_reg < 8; mii_reg++) - printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg)); - printk(".\n"); - */ + if (sis900_debug > 1) { + printk(KERN_INFO "%s:Tx queue start entry %d dirty entry %d.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + for (i = 0; i < NUM_TX_DESC; i++) + printk(KERN_INFO "%s: Tx descriptor %d is %8.8x.%s\n", + dev->name, i, (unsigned int)&tp->tx_buf[i], + i == tp->dirty_tx % NUM_TX_DESC ? + " (queue head)" : ""); + } /* Soft reset the chip. */ + //outb(RESET, ioaddr + cr); + /* Check that the chip has finished the reset. */ + /* + for (i = 1000; i > 0; i--) + if ((inb(ioaddr + cr) & RESET) == 0) + break; + */ - tp->cur_rx = 0; //?????? + tp->cur_rx = 0; /* Must enable Tx/Rx before setting transfer thresholds! */ /* set_rx_mode(dev); @@ -1145,10 +1134,6 @@ tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE]; tp->tx_buf[i].bufPhys = virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]); - /* - printk(KERN_INFO "tp->tx_buf[%d].bufPhys=%8.8x\n", - i, tp->tx_buf[i].bufPhys); - */ } /* Tx Descriptor */ @@ -1161,11 +1146,6 @@ tp->tx_buf[i].physAddr= virt_to_bus(&(tp->tx_buf[i].plink)); tp->tx_buf[i].cmdsts=0; - - /* - printk(KERN_INFO "tp->tx_buf[%d].PhysAddr=%8.8x\n", - i, tp->tx_buf[i].physAddr); - */ } /* Rx Buffer */ @@ -1173,10 +1153,6 @@ tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE]; tp->rx_buf[i].bufPhys = virt_to_bus(&tp->rx_bufs[i*RX_BUF_SIZE]); - /* - printk(KERN_INFO "tp->rx_buf[%d].bufPhys=%8.8x\n", - i, tp->rx_buf[i].bufPhys); - */ } /* Rx Descriptor */ @@ -1189,10 +1165,6 @@ tp->rx_buf[i].physAddr= virt_to_bus(&(tp->rx_buf[i].plink)); tp->rx_buf[i].cmdsts=RX_BUF_SIZE; - /* - printk(KERN_INFO "tp->tx_buf[%d].PhysAddr=%8.8x\n", - i, tp->tx_buf[i].physAddr); - */ } } @@ -1234,6 +1206,7 @@ tp->tx_buf[entry].cmdsts=(OWN | skb->len); + //tp->tx_buf[entry].plink = 0; outl(TxENA, ioaddr + cr); if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ clear_bit(0, (void*)&dev->tbusy); @@ -1347,11 +1320,6 @@ tp->stats.tx_errors++; if (txstatus & ABORT) { tp->stats.tx_aborted_errors++; - /* - outl((TX_DMA_BURST<<8)| - 0x03000001, - ioaddr + TxConfig); - */ } if (txstatus & NOCARRIER) tp->stats.tx_carrier_errors++; @@ -1366,27 +1334,21 @@ /* No count for tp->stats.tx_deferred */ #endif if (txstatus & UNDERRUN) { - if (sis900_debug > 1) + if (sis900_debug > 2) printk(KERN_INFO "Tx UnderRun\n"); - /* Add 64 to the Tx FIFO threshold. */ - /* - if (tp->tx_flag < 0x00300000) - tp->tx_flag+=0x00020000; - tp->stats.tx_fifo_errors++; - */ } tp->stats.collisions += (txstatus >> 16) & 0xF; #if LINUX_VERSION_CODE > 0x20119 tp->stats.tx_bytes += txstatus & DSIZE; #endif - if (sis900_debug > 1) + if (sis900_debug > 2) printk(KERN_INFO "Tx Transmit OK\n"); tp->stats.tx_packets++; } /* Free the original skb. */ - if (sis900_debug > 1) + if (sis900_debug > 2) printk(KERN_INFO "Free original skb\n"); dev_free_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; @@ -1404,15 +1366,16 @@ if (tp->tx_full && dirty_tx > tp->cur_tx-NUM_TX_DESC) { /* The ring is no longer full, clear tbusy. */ - //printk(KERN_INFO "Tx Ring NO LONGER Full\n"); + if (sis900_debug > 3) + printk(KERN_INFO "Tx Ring NO LONGER Full\n"); tp->tx_full = 0; dev->tbusy = 0; mark_bh(NET_BH); } tp->dirty_tx = dirty_tx; - if (sis900_debug > 1) - printk(KERN_INFO "TxOK release, tp->cur_tx:%d, tp->dirty:%d\n", + if (sis900_debug > 2) + printk(KERN_INFO "TxOK,tp->cur_tx:%d,tp->dirty:%d\n", tp->cur_tx, tp->dirty_tx); } // if (TxOK | TxERR) @@ -1424,25 +1387,18 @@ if (status == 0xffffffff) break; - if (status & (RxORN | RxERR)) tp->stats.rx_errors++; + if (status & RxORN) { tp->stats.rx_over_errors++; - /* - tp->cur_rx = - inw(ioaddr + RxBufAddr) % RX_BUF_LEN; - outw(tp->cur_rx - 16, ioaddr + RxBufPtr); - */ } } if (--boguscnt < 0) { printk(KERN_INFO "%s: Too much work at interrupt, " "IntrStatus=0x%4.4x.\n", dev->name, status); - /* Clear all interrupt sources. */ - //outw(0xffff, ioaddr + IntrStatus); break; } } while (1); @@ -1478,7 +1434,6 @@ int rx_size = rx_status & DSIZE; rx_size -= CRC_SIZE; - //printk(KERN_INFO "Rx OWN\n"); if (sis900_debug > 4) { int i; printk(KERN_INFO "%s: sis900_rx, rx status %8.8x," @@ -1510,21 +1465,11 @@ if (rx_status & (RUNT | TOOLONG)) tp->stats.rx_length_errors++; if (rx_status & CRCERR) tp->stats.rx_crc_errors++; - /* Reset the receiver, - based on RealTek recommendation. (Bug?) */ - /* - tp->cur_rx = 0; - outl(TxENA, ioaddr + cr); - outl(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); - outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | - (RX_DMA_BURST<<8), ioaddr + RxConfig); - */ } else { /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ struct sk_buff *skb; - //printk(KERN_INFO "Rx OK\n"); skb = dev_alloc_skb(rx_size + 2); if (skb == NULL) { printk(KERN_INFO "%s: Memory squeeze," @@ -1556,7 +1501,6 @@ memset(rx_bufs, 0xcc, 16); } */ - //printk(KERN_INFO "Frame Wrap....\n"); } else { #if 0 /* USE_IP_COPYSUM */ eth_copy_and_sum(skb, @@ -1578,7 +1522,6 @@ cur_rx = ((cur_rx+1) % NUM_RX_DESC); rx_status = tp->rx_buf[cur_rx].cmdsts; - //outw(cur_rx - 16, ioaddr + RxBufPtr); } // while if (sis900_debug > 4) printk(KERN_INFO "%s: Done sis900_rx(), current %4.4x " @@ -1635,7 +1578,7 @@ switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - data[0] = tp->phys[0] & 0x3f; + data[0] = tp->phys[tp->phy_idx]; /* Fall Through */ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); @@ -1654,6 +1597,7 @@ sis900_get_stats(struct device *dev) { struct sis900_private *tp = (struct sis900_private *)dev->priv; + return &tp->stats; } @@ -1709,16 +1653,26 @@ int *speed, int *duplex) { - u16 status; + u16 status, OurCap; *speed = HW_SPEED_10_MBPS; *duplex = FDX_CAPABLE_HALF_SELECTED; status = mdio_read(dev, phy_id, MII_ANLPAR); + OurCap = mdio_read(dev, phy_id, MII_ANAR); + if (sis900_debug > 1) { + printk(KERN_INFO "Link Part Status %4X\n", status); + printk(KERN_INFO "Our Status %4X\n", OurCap); + printk(KERN_INFO "Status Reg %4X\n", + mdio_read(dev, phy_id, MII_STATUS)); + } + status &= OurCap; if ( !( status & (MII_NWAY_T|MII_NWAY_T_FDX | MII_NWAY_TX | MII_NWAY_TX_FDX ))) { -// printk(KERN_INFO "%s: Link Partner not detected.\n", dev->name); + if (sis900_debug > 1) { + printk(KERN_INFO "The other end NOT support NWAY...\n"); + } while (( status = mdio_read(dev, phy_id, 18)) & 0x4000) ; while (( status = mdio_read(dev, phy_id, 18)) & 0x0020) ; if (status & 0x80) @@ -1734,7 +1688,10 @@ "full" : "half"); } } else { -// printk(KERN_INFO "%s: Link Partner detected\n", dev->name); + if (sis900_debug > 1) { + printk(KERN_INFO "The other end support NWAY...\n"); + } + if (status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) { *duplex = FDX_CAPABLE_FULL_SELECTED; } @@ -1756,19 +1713,56 @@ static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed) { - u16 status; + u16 status, retnVal; + if (sis900_debug > 1) { + printk(KERN_INFO "AutoNegotiate...\n"); + } mdio_write(dev, phy_id, MII_CONTROL, 0); - mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | - MIICNTL_RST_AUTO); - elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, FALSE,&status); - elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, TRUE, &status); - elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, TRUE, &status); + mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | MIICNTL_RST_AUTO); + retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, + FALSE,&status); + if (!retnVal) { + printk(KERN_INFO "Not wait for Reset Complete\n"); + } + retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, + TRUE, &status); + if (!retnVal) { + printk(KERN_INFO "Not wait for AutoNego Complete\n"); + } + retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, + TRUE, &status); + if (!retnVal) { + printk(KERN_INFO "Not wait for Link Complete\n"); + } if (status & MIISTAT_LINK) { elPMDreadMode(dev, phy_id, speed, duplex); elSetMediaType(dev, *speed, *duplex); } return(status); +} + +static void elSetCapability(struct device *dev, int phy_id, + int duplex, int speed) +{ + u16 cap = ( MII_NWAY_T | MII_NWAY_T_FDX | + MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_CSMA_CD ); + + if (speed != 100) { + cap &= ~( MII_NWAY_TX | MII_NWAY_TX_FDX ); + if (sis900_debug > 1) { + printk(KERN_INFO "UNSET 100Mbps\n"); + } + } + + if (!duplex) { + cap &= ~( MII_NWAY_T_FDX | MII_NWAY_TX_FDX ); + if (sis900_debug > 1) { + printk(KERN_INFO "UNSET full-duplex\n"); + } + } + + mdio_write(dev, phy_id, MII_ANAR, cap); } static void elSetMediaType(struct device *dev, int speed, int duplex) diff -u --recursive --new-file v2.2.11/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c --- v2.2.11/linux/drivers/pci/oldproc.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/pci/oldproc.c Wed Aug 25 17:29:48 1999 @@ -535,6 +535,7 @@ 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( COMPUTONE, COMPUTONE_IP2EX, "Computone IntelliPort Plus"), DEVICE( INTEL, INTEL_82443LX_1,"440LX - 82443LX PAC AGP"), DEVICE( INTEL, INTEL_82443BX_0,"440BX - 82443BX Host"), DEVICE( INTEL, INTEL_82443BX_1,"440BX - 82443BX AGP"), @@ -830,6 +831,7 @@ case PCI_VENDOR_ID_NVIDIA_SGS: return "NVidia/SGS Thomson"; case PCI_VENDOR_ID_CBOARDS: return "ComputerBoards"; case PCI_VENDOR_ID_SYMPHONY: return "Symphony"; + case PCI_VENDOR_ID_COMPUTONE: return "Computone Corporation"; case PCI_VENDOR_ID_TEKRAM: return "Tekram"; case PCI_VENDOR_ID_3DLABS: return "3Dlabs"; case PCI_VENDOR_ID_AVANCE: return "Avance"; diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.2.11/linux/drivers/scsi/Config.in Wed Jun 9 16:59:34 1999 +++ linux/drivers/scsi/Config.in Wed Aug 25 17:29:48 1999 @@ -34,9 +34,6 @@ dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI dep_tristate 'AMI MegaRAID support' CONFIG_SCSI_MEGARAID $CONFIG_SCSI -if [ "$CONFIG_SCSI_MEGARAID" != "n" ]; then - bool ' Concurrent IO commands on MegaRAID' CONFIG_MEGARAID_MULTI_IO -fi dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c --- v2.2.11/linux/drivers/scsi/aha152x.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/scsi/aha152x.c Wed Aug 25 17:29:48 1999 @@ -1394,9 +1394,7 @@ if (ptr && !ptr->device->soft_reset) { ptr->host_scribble = NULL; ptr->result = DID_RESET << 16; - spin_lock_irqsave(&io_request_lock, flags); ptr->scsi_done(CURRENT_SC); - spin_unlock_irqrestore(&io_request_lock, flags); CURRENT_SC = NULL; } save_flags(flags); @@ -1414,9 +1412,7 @@ ptr->host_scribble = NULL; ptr->result = DID_RESET << 16; - spin_lock_irqsave(&io_request_lock, flags); ptr->scsi_done(ptr); - spin_unlock_irqrestore(&io_request_lock, flags); ptr = next; } else { diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v2.2.11/linux/drivers/scsi/hosts.c Mon Apr 12 09:51:04 1999 +++ linux/drivers/scsi/hosts.c Wed Aug 25 17:29:48 1999 @@ -327,6 +327,10 @@ #include "jazz_esp.h" #endif +#ifdef CONFIG_IPHASE5526 +#include "../net/fc/iph5526_scsi.h" +#endif + /* * Moved ppa driver to the end of the probe list * since it is a removable host adapter. @@ -588,6 +592,9 @@ #ifdef CONFIG_SCSI_POWERTECSCSI POWERTECSCSI, #endif +#endif +#ifdef CONFIG_IPHASE5526 + IPH5526_SCSI_FC, #endif /* "Removable host adapters" below this line (Parallel Port/USB/other) */ #ifdef CONFIG_SCSI_PPA diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/ini9100u.c linux/drivers/scsi/ini9100u.c --- v2.2.11/linux/drivers/scsi/ini9100u.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/scsi/ini9100u.c Wed Aug 25 17:29:48 1999 @@ -118,19 +118,13 @@ #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) #include -#include #include -#include #include -#include -#include #if LINUX_VERSION_CODE <= CVT_LINUX_VERSION(2,1,92) #include #endif #include -#include #include -#include #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,23) #include #endif @@ -138,39 +132,28 @@ #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) #include #endif -#include "sd.h" -#include "scsi.h" -#include "hosts.h" -#include "ini9100u.h" #include -#include #include #else -#include #include #include +#include +#include "../block/blk.h" +#endif + +#include #include #include - #include #include -#include #include -#include "../block/blk.h" #include "scsi.h" #include "sd.h" #include "hosts.h" #include #include "ini9100u.h" -#endif - -#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93) -#ifdef CONFIG_PCI -#include -#endif -#endif #ifdef DEBUG_i91u unsigned int i91u_debug = DEBUG_DEFAULT; diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/inia100.c linux/drivers/scsi/inia100.c --- v2.2.11/linux/drivers/scsi/inia100.c Wed Feb 24 16:27:54 1999 +++ linux/drivers/scsi/inia100.c Wed Aug 25 17:29:48 1999 @@ -73,19 +73,13 @@ #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) #include -#include #include -#include #include -#include -#include #include -#include #if LINUX_VERSION_CODE <= CVT_LINUX_VERSION(2,1,92) #include #endif #include -#include #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,23) #include #endif @@ -93,33 +87,27 @@ #if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) #include #endif -#include "sd.h" -#include "scsi.h" -#include "hosts.h" -#include "inia100.h" #include -#include - #else -#include #include #include +#include +#include "../block/blk.h" +#endif + +#include #include #include - #include #include -#include #include -#include "../block/blk.h" #include "scsi.h" #include "sd.h" #include "hosts.h" #include #include "inia100.h" -#endif #ifdef MODULE Scsi_Host_Template driver_template = INIA100; diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/mac53c94.c linux/drivers/scsi/mac53c94.c --- v2.2.11/linux/drivers/scsi/mac53c94.c Fri May 8 00:22:12 1998 +++ linux/drivers/scsi/mac53c94.c Wed Aug 25 17:29:48 1999 @@ -85,7 +85,9 @@ if (host == 0) panic("couldn't register 53c94 host"); host->unique_id = nfscs; +#ifndef MODULE note_scsi_host(node, host); +#endif state = (struct fsc_state *) host->hostdata; if (state == 0) diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/megaraid.c linux/drivers/scsi/megaraid.c --- v2.2.11/linux/drivers/scsi/megaraid.c Mon Aug 9 16:05:56 1999 +++ linux/drivers/scsi/megaraid.c Wed Aug 25 17:29:48 1999 @@ -9,14 +9,12 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version : 1.01 + * Version : 1.04 * * Description: Linux device driver for AMI MegaRAID controller * - * Supported controllers: MegaRAID 418, 428, 438, 466, 762 + * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 490 * - * Maintainer: Jeff L Jones - * Xi Shine Chen * History: * * Version 0.90: @@ -93,18 +91,41 @@ * Fixed bug in mega_cmd_done() for megamgr control commands, * the host_byte in the result code from the scsi request to * scsi midlayer is set to DID_BAD_TARGET when adapter's - * returned codes are 0xF0 and oxF4. + * returned codes are 0xF0 and 0xF4. + * + * Version 1.02: + * Fixed the tape drive bug by extending the adapter timeout value + * for passthrough command to 60 seconds in mega_build_cmd(). + * + * Version 1.03: + * Fixed Madrona support. + * Changed the adapter timeout value from 60 sec in 1.02 to 10 min + * for bigger and slower tape drive. + * Added driver version printout at driver loadup time + * + * Version 1.04 + * Added code for 40 ld FW support. + * Added new ioctl command 0x81 to support NEW_READ/WRITE_CONFIG with + * data area greater than 4 KB, which is the upper bound for data + * tranfer through scsi_ioctl interface. + * The addtional 32 bit field for 64bit address in the newly defined + * mailbox64 structure is set to 0 at this point. * * BUGS: * Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that * fails to detect the controller as a pci device on the system. * - * Timeout period for mid scsi layer is too short for - * this controller. Must be increased or Aborts will occur. + * Timeout period for upper scsi layer, i.e. SD_TIMEOUT in + * /drivers/scsi/sd.c, is too short for this controller. SD_TIMEOUT + * value must be increased to (30 * HZ) otherwise false timeouts + * will occur in the upper layer. * *===================================================================*/ #define CRLFSTR "\n" +#define IOCTL_CMD_NEW 0x81 + +#define MEGARAID_VERSION "v1.04 (August 16, 1999)" #include #include @@ -148,6 +169,7 @@ #include #include +#include #include "sd.h" #include "scsi.h" @@ -213,22 +235,22 @@ spin_unlock_irqrestore(&mega_lock,cpuflag);\ }; -u_long RDINDOOR (mega_host_config * megaCfg) +u32 RDINDOOR (mega_host_config * megaCfg) { return readl (megaCfg->base + 0x20); } -void WRINDOOR (mega_host_config * megaCfg, u_long value) +void WRINDOOR (mega_host_config * megaCfg, u32 value) { writel (value, megaCfg->base + 0x20); } -u_long RDOUTDOOR (mega_host_config * megaCfg) +u32 RDOUTDOOR (mega_host_config * megaCfg) { return readl (megaCfg->base + 0x2C); } -void WROUTDOOR (mega_host_config * megaCfg, u_long value) +void WROUTDOOR (mega_host_config * megaCfg, u32 value) { writel (value, megaCfg->base + 0x2C); } @@ -244,7 +266,7 @@ mega_scb * scb, int intr); static int build_sglist (mega_host_config * megaCfg, mega_scb * scb, - u_long * buffer, u_long * length); + u32 * buffer, u32 * length); static int mega_busyWaitMbox(mega_host_config *); static void mega_runpendq (mega_host_config *); @@ -252,6 +274,9 @@ static void mega_cmd_done (mega_host_config *, mega_scb *, int); static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt); static inline void freeSgList(mega_host_config *megaCfg); +static void mega_Convert8ldTo40ld( mega_RAIDINQ *inquiry, + mega_Enquiry3 *enquiry3, + megaRaidProductInfo *productInfo ); /* set SERDEBUG to 1 to enable serial debugging */ #define SERDEBUG 0 @@ -269,9 +294,9 @@ *================================================================ */ -/* Use "megaraid=skipXX" to prohibit driver from scanning XX scsi id - on each channel. Used for Madrona motherboard, where SAF_TE - processor id cannot be scanned */ +/* Use "megaraid=skipXX" as LILO option to prohibit driver from scanning + XX scsi id on each channel. Used for Madrona motherboard, where SAF_TE + processor id cannot be scanned */ static char *megaraid; #if LINUX_VERSION_CODE > 0x20100 #ifdef MODULE @@ -281,10 +306,10 @@ static int skip_id; static int numCtlrs = 0; -static mega_host_config *megaCtlrs[12] = {0}; +static mega_host_config *megaCtlrs[FC_MAX_CHANNELS] = {0}; #if DEBUG -static u_long maxCmdTime = 0; +static u32 maxCmdTime = 0; #endif static mega_scb *pLastScb = NULL; @@ -512,8 +537,8 @@ while(1); } - islogical = (SCpnt->channel == megaCfg->host->max_channel && - SCpnt->target == 0); + islogical = (SCpnt->channel == megaCfg->host->max_channel); + if (SCpnt->cmnd[0] == INQUIRY && ((((u_char *) SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) && !islogical) { @@ -523,8 +548,7 @@ /* clear result; otherwise, success returns corrupt value */ SCpnt->result = 0; -if (SCpnt->cmnd[0] & 0x80) /* ioctl from megamgr */{ - /*printk(KERN_WARNING "***megamgr cmnd status= %x h\n ", status); */ +if ((SCpnt->cmnd[0] & 0x80) ) {/* i.e. ioctl cmd such as 0x80, 0x81 of megamgr*/ switch (status) { case 0xF0: case 0xF4: @@ -556,11 +580,15 @@ SCpnt->result |= (DID_BUS_BUSY << 16)|status; break; default: - SCpnt->result |= (DID_BAD_TARGET << 16); + SCpnt->result |= (DID_BAD_TARGET << 16)|status; break; } } - freeSCB(megaCfg, pScb); + if ( SCpnt->cmnd[0]!=IOCTL_CMD_NEW ) + /* not IOCTL_CMD_NEW SCB, freeSCB()*/ + /* For IOCTL_CMD_NEW SCB, delay freeSCB() in megaraid_queue() + * after copy data back to user space*/ + freeSCB(megaCfg, pScb); /* Add Scsi_Command to end of completed queue */ ENQUEUE_NL(SCpnt, Scsi_Cmnd, qCompleted, host_scribble); @@ -582,18 +610,14 @@ mega_passthru *pthru; long seg; char islogical; + char lun = SCpnt->lun; - if (SCpnt == NULL) { - printk("NULL SCpnt in mega_build_cmd!\n"); - while(1); - } - - if (SCpnt->cmnd[0] & 0x80) /* ioctl from megamgr */ + if ((SCpnt->cmnd[0] == 0x80) || (SCpnt->cmnd[0] == IOCTL_CMD_NEW) ) /* ioctl */ return mega_ioctl (megaCfg, SCpnt); + + islogical = (SCpnt->channel == megaCfg->host->max_channel); - islogical = (SCpnt->channel == megaCfg->host->max_channel && SCpnt->target == 0); - - if (!islogical && SCpnt->lun != 0) { + if (!islogical && lun != 0) { SCpnt->result = (DID_BAD_TARGET << 16); callDone (SCpnt); return NULL; @@ -605,6 +629,16 @@ return NULL; } + if ( islogical ) { + lun = (SCpnt->target * 8) + lun; +#if 1 + if ( lun > FC_MAX_LOGICAL_DRIVES ){ + SCpnt->result = (DID_BAD_TARGET << 16); + callDone (SCpnt); + return NULL; + } +#endif + } /*----------------------------------------------------- * * Logical drive commands @@ -641,7 +675,7 @@ pthru->ars = 1; pthru->reqsenselen = 14; pthru->islogical = 1; - pthru->logdrv = SCpnt->lun; + pthru->logdrv = lun; pthru->cdblen = SCpnt->cmd_len; pthru->dataxferaddr = virt_to_bus (SCpnt->request_buffer); pthru->dataxferlen = SCpnt->request_bufflen; @@ -666,37 +700,37 @@ mbox = (mega_mailbox *) & pScb->mboxData; memset (mbox, 0, sizeof (pScb->mboxData)); - mbox->logdrv = SCpnt->lun; + mbox->logdrv = lun; mbox->cmd = (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10) ? MEGA_MBOXCMD_LREAD : MEGA_MBOXCMD_LWRITE; /* 6-byte */ if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) { mbox->numsectors = - (u_long) SCpnt->cmnd[4]; + (u32) SCpnt->cmnd[4]; mbox->lba = - ((u_long) SCpnt->cmnd[1] << 16) | - ((u_long) SCpnt->cmnd[2] << 8) | - (u_long) SCpnt->cmnd[3]; + ((u32) SCpnt->cmnd[1] << 16) | + ((u32) SCpnt->cmnd[2] << 8) | + (u32) SCpnt->cmnd[3]; mbox->lba &= 0x1FFFFF; } /* 10-byte */ if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) { mbox->numsectors = - (u_long) SCpnt->cmnd[8] | - ((u_long) SCpnt->cmnd[7] << 8); + (u32) SCpnt->cmnd[8] | + ((u32) SCpnt->cmnd[7] << 8); mbox->lba = - ((u_long) SCpnt->cmnd[2] << 24) | - ((u_long) SCpnt->cmnd[3] << 16) | - ((u_long) SCpnt->cmnd[4] << 8) | - (u_long) SCpnt->cmnd[5]; + ((u32) SCpnt->cmnd[2] << 24) | + ((u32) SCpnt->cmnd[3] << 16) | + ((u32) SCpnt->cmnd[4] << 8) | + (u32) SCpnt->cmnd[5]; } /* Calculate Scatter-Gather info */ mbox->numsgelements = build_sglist (megaCfg, pScb, - (u_long *) & mbox->xferaddr, - (u_long *) & seg); + (u32 *) & mbox->xferaddr, + (u32 *) & seg); return pScb; @@ -723,18 +757,20 @@ memset (mbox, 0, sizeof (pScb->mboxData)); memset (pthru, 0, sizeof (mega_passthru)); - pthru->timeout = 0; + pthru->timeout = 2; /*set adapter timeout value to 10 min. for tape drive*/ + /* 0=6sec/1=60sec/2=10min/3=3hrs */ pthru->ars = 1; pthru->reqsenselen = 14; pthru->islogical = 0; - pthru->channel = SCpnt->channel; - pthru->target = SCpnt->target; + pthru->channel = (megaCfg->flag & BOARD_40LD) ? 0 : SCpnt->channel; + pthru->target = (megaCfg->flag & BOARD_40LD) ? /*BOARD_40LD*/ + (SCpnt->channel<<4)|SCpnt->target : SCpnt->target; pthru->cdblen = SCpnt->cmd_len; memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len); pthru->numsgelements = build_sglist (megaCfg, pScb, - (u_long *) & pthru->dataxferaddr, - (u_long *) & pthru->dataxferlen); + (u32 *) & pthru->dataxferaddr, + (u32 *) & pthru->dataxferlen); /* Initialize mailbox */ mbox->cmd = MEGA_MBOXCMD_PASSTHRU; @@ -754,6 +790,7 @@ mega_ioctl_mbox *mbox; mega_mailbox *mailbox; mega_passthru *pthru; + u8 *mboxdata; long seg; unsigned char *data = (unsigned char *)SCpnt->request_buffer; int i; @@ -772,6 +809,7 @@ printk("......\n"); #endif + mboxdata = (u8 *) & pScb->mboxData; mbox = (mega_ioctl_mbox *) & pScb->mboxData; mailbox = (mega_mailbox *) & pScb->mboxData; memset (mailbox, 0, sizeof (pScb->mboxData)); @@ -793,10 +831,9 @@ mailbox->cmd = MEGA_MBOXCMD_PASSTHRU; mailbox->xferaddr = virt_to_bus (pthru); - pthru->numsgelements = build_sglist (megaCfg, pScb, - (u_long *) & pthru->dataxferaddr, - (u_long *) & pthru->dataxferlen); + (u32 *) & pthru->dataxferaddr, + (u32 *) & pthru->dataxferlen); for (i=0;i<(SCpnt->request_bufflen-cdblen-7);i++) { data[i] = data[i+cdblen+7]; @@ -806,18 +843,60 @@ } /* else normal (nonpassthru) command */ + if (SCpnt->cmnd[0] == IOCTL_CMD_NEW) { + /* use external data area for large xfers */ + /* If cmnd[0] is set to IOCTL_CMD_NEW then * + * cmnd[4..7] = external user buffer * + * cmnd[8..11] = length of buffer * + * */ + char *kern_area; + char *user_area = *((char **)&SCpnt->cmnd[4]); + u32 xfer_size = *((u32 *)&SCpnt->cmnd[8]); + if (verify_area(VERIFY_READ, user_area, xfer_size)) { + printk("megaraid: Got bad user address.\n"); + SCpnt->result = (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } + kern_area = kmalloc(xfer_size, GFP_ATOMIC | GFP_DMA); + if (kern_area == NULL) { + printk("megaraid: Couldn't allocate kernel mem.\n"); + SCpnt->result = (DID_ERROR << 16); + callDone (SCpnt); + return NULL; + } + copy_from_user(kern_area,user_area,xfer_size); + pScb->kern_area = kern_area; + } + mbox->cmd = data[0]; mbox->channel = data[1]; mbox->param = data[2]; mbox->pad[0] = data[3]; mbox->logdrv = data[4]; - mbox->numsgelements = build_sglist (megaCfg, pScb, - (u_long *) & mbox->xferaddr, - (u_long *) & seg); + if(SCpnt->cmnd[0] == IOCTL_CMD_NEW) { + if(data[0]==DCMD_FC_CMD){ /*i.e. 0xA1, then override some mbox data */ + *(mboxdata+0) = data[0]; /*mailbox byte 0: DCMD_FC_CMD*/ + *(mboxdata+2) = data[2]; /*sub command*/ + *(mboxdata+3) = 0; /*number of elements in SG list*/ + mbox->xferaddr /*i.e. mboxdata byte 0x8 to 0xb*/ + = virt_to_bus(pScb->kern_area); + } + else{ + mbox->xferaddr = virt_to_bus(pScb->kern_area); + mbox->numsgelements = 0; + } + } + else { + + mbox->numsgelements = build_sglist (megaCfg, pScb, + (u32 *) & mbox->xferaddr, + (u32 *) & seg); - for (i=0;i<(SCpnt->request_bufflen-6);i++) { - data[i] = data[i+6]; + for (i=0;i<(SCpnt->request_bufflen-6);i++) { + data[i] = data[i+6]; + } } return (pScb); @@ -846,7 +925,7 @@ { mega_host_config *megaCfg; u_char byte, idx, sIdx, tmpBox[MAILBOX_SIZE]; - u_long dword; + u32 dword; mega_mailbox *mbox; mega_scb *pScb; long flags; @@ -930,8 +1009,13 @@ printk("Received aborted SCB! %u\n", (int)((jiffies)-pScb->isrcount)); } + if (*(pScb->SCpnt->cmnd)==IOCTL_CMD_NEW) + { /* external user buffer */ + up(&pScb->sem); + } /* Mark command as completed */ mega_cmd_done(megaCfg, pScb, qStatus); + } } @@ -945,6 +1029,7 @@ spin_lock_irqsave(&mega_lock, flags); mega_runpendq(megaCfg); spin_unlock_irqrestore(&mega_lock,flags); + } #if LINUX_VERSION_CODE >= 0x20100 @@ -978,6 +1063,11 @@ * u_char *mboxData - Mailbox area, 16 bytes * mega_scb *pScb - SCB posting (or NULL if N/A) * int intr - if 1, interrupt, 0 is blocking + * Return Value: (added on 7/26 for 40ld/64bit) + * -1: the command was not actually issued out + * othercases: + * intr==0, return ScsiStatus, i.e. mbox->status + * intr==1, return 0 *===================================================== */ static int megaIssueCmd (mega_host_config * megaCfg, @@ -987,12 +1077,16 @@ { mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; u_char byte; - u_long cmdDone; + u32 cmdDone; Scsi_Cmnd *SCpnt; + u32 phys_mbox; + u8 retval=-1; mboxData[0x1] = (pScb ? pScb->idx + 1: 0x0); /* Set cmdid */ mboxData[0xF] = 1; /* Set busy */ + phys_mbox = virt_to_bus (megaCfg->mbox); + #if 0 if (intr && mbox->busy) { return 0; @@ -1005,7 +1099,7 @@ /* Wait until mailbox is free */ while (mega_busyWaitMbox (megaCfg)) { - printk("Blocked mailbox!!\n"); + printk("Blocked mailbox......!!\n"); udelay(1000); #if DEBUG @@ -1022,12 +1116,13 @@ SCpnt->result = (DID_ABORT << 16); callDone(SCpnt); - return 0; + return -1; } pLastScb = pScb; /* Copy mailbox data into host structure */ + megaCfg->mbox64->xferSegment = 0; memcpy (mbox, mboxData, 16); /* Kick IO */ @@ -1037,20 +1132,22 @@ if (megaCfg->flag & BOARD_QUARTZ) { mbox->mraid_poll = 0; mbox->mraid_ack = 0; - WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x1); + WRINDOOR (megaCfg, phys_mbox | 0x1); } else { ENABLE_INTR (megaCfg->host->io_port); ISSUE_COMMAND (megaCfg->host->io_port); } pScb->state = SCB_ISSUED; + + retval=0; } else { /* Issue non-ISR (blocking) command */ disable_irq(megaCfg->host->irq); if (megaCfg->flag & BOARD_QUARTZ) { mbox->mraid_poll = 0; mbox->mraid_ack = 0; - WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x1); + WRINDOOR (megaCfg, phys_mbox | 0x1); while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234); WROUTDOOR (megaCfg, cmdDone); @@ -1060,7 +1157,7 @@ mega_rundoneq (); } - WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2); + WRINDOOR (megaCfg, phys_mbox | 0x2); while (RDINDOOR (megaCfg) & 0x2); } @@ -1085,20 +1182,21 @@ } enable_irq(megaCfg->host->irq); + retval=mbox->status; } while (mega_busyWaitMbox (megaCfg)) { - printk("Blocked mailbox on exit!\n"); + printk("Blocked mailbox on exit......!\n"); udelay(1000); } - return 0; + return retval; } /*------------------------------------------------------------------- * Copies data to SGLIST *-------------------------------------------------------------------*/ static int build_sglist (mega_host_config * megaCfg, mega_scb * scb, - u_long * buffer, u_long * length) + u32 * buffer, u32 * length) { struct scatterlist *sgList; int idx; @@ -1106,21 +1204,21 @@ /* Scatter-gather not used */ if (scb->SCpnt->use_sg == 0) { *buffer = virt_to_bus (scb->SCpnt->request_buffer); - *length = (u_long) scb->SCpnt->request_bufflen; + *length = (u32) scb->SCpnt->request_bufflen; return 0; } sgList = (struct scatterlist *) scb->SCpnt->request_buffer; if (scb->SCpnt->use_sg == 1) { *buffer = virt_to_bus (sgList[0].address); - *length = (u_long) sgList[0].length; + *length = (u32) sgList[0].length; return 0; } /* Copy Scatter-Gather list info into controller structure */ for (idx = 0; idx < scb->SCpnt->use_sg; idx++) { scb->sgList[idx].address = virt_to_bus (sgList[idx].address); - scb->sgList[idx].length = (u_long) sgList[idx].length; + scb->sgList[idx].length = (u32) sgList[idx].length; } /* Reset pointer and length fields */ @@ -1147,12 +1245,13 @@ * 10 01 numstatus byte * 11 01 status byte *--------------------------------------------------------------------*/ -static int mega_register_mailbox (mega_host_config * megaCfg, u_long paddr) +static int mega_register_mailbox (mega_host_config * megaCfg, u32 paddr) { /* align on 16-byte boundry */ - megaCfg->mbox = &megaCfg->mailbox; - megaCfg->mbox = (mega_mailbox *) ((((ulong) megaCfg->mbox) + 16) & 0xfffffff0); - paddr = (paddr + 16) & 0xfffffff0; + megaCfg->mbox = &megaCfg->mailbox64.mailbox; + megaCfg->mbox = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xfffffff0); + megaCfg->mbox64 = (mega_mailbox64 *) (megaCfg->mbox - 4); + paddr = (paddr + 4 + 16) & 0xfffffff0; /* Register mailbox area with the firmware */ if (megaCfg->flag & BOARD_QUARTZ) { @@ -1170,83 +1269,161 @@ return 0; } + +/*--------------------------------------------------------------------------- + * mega_Convert8ldTo40ld() -- takes all info in AdapterInquiry structure and + * puts it into ProductInfo and Enquiry3 structures for later use + *---------------------------------------------------------------------------*/ +static void mega_Convert8ldTo40ld( mega_RAIDINQ *inquiry, + mega_Enquiry3 *enquiry3, + megaRaidProductInfo *productInfo ) +{ + int i; + + productInfo->MaxConcCmds = inquiry->AdpInfo.MaxConcCmds; + enquiry3->rbldRate = inquiry->AdpInfo.RbldRate; + productInfo->SCSIChanPresent = inquiry->AdpInfo.ChanPresent; + for (i=0;i<4;i++) { + productInfo->FwVer[i] = inquiry->AdpInfo.FwVer[i]; + productInfo->BiosVer[i] = inquiry->AdpInfo.BiosVer[i]; + } + enquiry3->cacheFlushInterval = inquiry->AdpInfo.CacheFlushInterval; + productInfo->DramSize = inquiry->AdpInfo.DramSize; + + enquiry3->numLDrv = inquiry->LogdrvInfo.NumLDrv; + for (i=0;ilDrvSize[i] = inquiry->LogdrvInfo.LDrvSize[i]; + enquiry3->lDrvProp[i] = inquiry->LogdrvInfo.LDrvProp[i]; + enquiry3->lDrvState[i] = inquiry->LogdrvInfo.LDrvState[i]; + } + + for (i=0;i<(MAX_PHYSICAL_DRIVES);i++) { + enquiry3->pDrvState[i] = inquiry->PhysdrvInfo.PDrvState[i]; + } +} + + /*------------------------------------------------------------------- * Issue an adapter info query to the controller *-------------------------------------------------------------------*/ static int mega_i_query_adapter (mega_host_config * megaCfg) { - mega_RAIDINQ *adapterInfo; + mega_Enquiry3 *enquiry3Pnt; mega_mailbox *mbox; u_char mboxData[16]; - u_long paddr; + u32 paddr; + u8 retval; spin_lock_init (&mega_lock); - /* Initialize adapter inquiry */ + /* Initialize adapter inquiry mailbox*/ paddr = virt_to_bus (megaCfg->mega_buffer); mbox = (mega_mailbox *) mboxData; memset ((void *) megaCfg->mega_buffer, 0, sizeof (megaCfg->mega_buffer)); memset (mbox, 0, 16); - /* Initialize mailbox registers */ - mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ; - mbox->xferaddr = paddr; +/* + * Try to issue Enquiry3 command + * if not suceeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and + * update enquiry3 structure + */ + mbox->xferaddr = virt_to_bus ( (void*) megaCfg->mega_buffer); + /* Initialize mailbox databuffer addr */ + enquiry3Pnt = (mega_Enquiry3 *) megaCfg->mega_buffer; + /* point mega_Enguiry3 to the data buf */ + + mboxData[0]=FC_NEW_CONFIG ; /* i.e. mbox->cmd=0xA1 */ + mboxData[2]=NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ + mboxData[3]=ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ /* Issue a blocking command to the card */ - megaIssueCmd (megaCfg, mboxData, NULL, 0); + if ( (retval=megaIssueCmd(megaCfg, mboxData, NULL, 0)) != 0 ) + { /* the adapter does not support 40ld*/ - /* Initialize host/local structures with Adapter info */ - adapterInfo = (mega_RAIDINQ *) megaCfg->mega_buffer; - megaCfg->host->max_channel = adapterInfo->AdpInfo.ChanPresent; -/* megaCfg->host->max_id = adapterInfo->AdpInfo.MaxTargPerChan; */ - megaCfg->host->max_id = 16; /* max targets/chan */ - megaCfg->numldrv = adapterInfo->LogdrvInfo.NumLDrv; + mega_RAIDINQ adapterInquiryData; + mega_RAIDINQ *adapterInquiryPnt = &adapterInquiryData; -#if 0 - printk ("KERN_DEBUG ---- Logical drive info ----\n"); + mbox->xferaddr = virt_to_bus ( (void*) adapterInquiryPnt); + + mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ; /*issue old 0x05 command to adapter*/ + /* Issue a blocking command to the card */; + retval=megaIssueCmd (megaCfg, mboxData, NULL, 0); + + /*update Enquiry3 and ProductInfo structures with mega_RAIDINQ structure*/ + mega_Convert8ldTo40ld( adapterInquiryPnt, + enquiry3Pnt, + (megaRaidProductInfo * ) &megaCfg->productInfo ); + + } + else{ /* adapter supports 40ld */ + megaCfg->flag |= BOARD_40LD; + + /*get productInfo, which is static information and will be unchanged*/ + mbox->xferaddr = virt_to_bus ( (void*) &megaCfg->productInfo ); + + mboxData[0]=FC_NEW_CONFIG ; /* i.e. mbox->cmd=0xA1 */ + mboxData[2]=NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ + + if( (retval=megaIssueCmd(megaCfg, mboxData, NULL, 0)) != 0 ) + printk("ami:Product_info (0x0E) cmd failed with error: %d\n", retval); + + } + + megaCfg->host->max_channel = megaCfg->productInfo.SCSIChanPresent; + megaCfg->host->max_id = 16; /* max targets per channel */ + /*(megaCfg->flag & BOARD_40LD)?FC_MAX_TARGETS_PER_CHANNEL:MAX_TARGET+1;*/ + megaCfg->host->max_lun = /* max lun */ + (megaCfg->flag & BOARD_40LD) ? FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES; + + megaCfg->numldrv = enquiry3Pnt->numLDrv; + megaCfg->max_cmds = megaCfg->productInfo.MaxConcCmds; + +#if 0 + int i; + printk (KERN_DEBUG "---- Logical drive info from enquiry3 struct----\n"); for (i = 0; i < megaCfg->numldrv; i++) { - printk ("%d: size: %ld prop: %x state: %x\n", i, - adapterInfo->LogdrvInfo.LDrvSize[i], - adapterInfo->LogdrvInfo.LDrvProp[i], - adapterInfo->LogdrvInfo.LDrvState[i]); + printk ("%d: size: %d prop: %x state: %x\n", i, + enquiry3Pnt->lDrvSize[i], + enquiry3Pnt->lDrvProp[i], + enquiry3Pnt->lDrvState[i]); } + printk (KERN_DEBUG "---- Physical drive info ----\n"); - for (i = 0; i < MAX_PHYSICAL_DRIVES; i++) { + for (i = 0; i < FC_MAX_PHYSICAL_DEVICES; i++) { if (i && !(i % 8)) printk ("\n"); - printk ("%d: %x ", i, adapterInfo->PhysdrvInfo.PDrvState[i]); + printk ("%d: %x ", i, enquiry3Pnt->pDrvState[i]); } printk ("\n"); #endif - megaCfg->max_cmds = adapterInfo->AdpInfo.MaxConcCmds; - #ifdef HP /* use HP firmware and bios version encoding */ sprintf (megaCfg->fwVer, "%c%d%d.%d%d", - adapterInfo->AdpInfo.FwVer[2], - adapterInfo->AdpInfo.FwVer[1] >> 8, - adapterInfo->AdpInfo.FwVer[1] & 0x0f, - adapterInfo->AdpInfo.FwVer[2] >> 8, - adapterInfo->AdpInfo.FwVer[2] & 0x0f); + megaCfg->productInfo.FwVer[2], + megaCfg->productInfo.FwVer[1] >> 8, + megaCfg->productInfo.FwVer[1] & 0x0f, + megaCfg->productInfo.FwVer[2] >> 8, + megaCfg->productInfo.FwVer[2] & 0x0f); sprintf (megaCfg->biosVer, "%c%d%d.%d%d", - adapterInfo->AdpInfo.BiosVer[2], - adapterInfo->AdpInfo.BiosVer[1] >> 8, - adapterInfo->AdpInfo.BiosVer[1] & 0x0f, - adapterInfo->AdpInfo.BiosVer[2] >> 8, - adapterInfo->AdpInfo.BiosVer[2] & 0x0f); + megaCfg->productInfo.BiosVer[2], + megaCfg->productInfo.BiosVer[1] >> 8, + megaCfg->productInfo.BiosVer[1] & 0x0f, + megaCfg->productInfo.BiosVer[2] >> 8, + megaCfg->productInfo.BiosVer[2] & 0x0f); #else - memcpy (megaCfg->fwVer, adapterInfo->AdpInfo.FwVer, 4); + memcpy (megaCfg->fwVer, megaCfg->productInfo.FwVer, 4); megaCfg->fwVer[4] = 0; - memcpy (megaCfg->biosVer, adapterInfo->AdpInfo.BiosVer, 4); + memcpy (megaCfg->biosVer, megaCfg->productInfo.BiosVer, 4); megaCfg->biosVer[4] = 0; #endif - printk (KERN_INFO "megaraid: [%s:%s] detected %d logical drives" CRLFSTR, - megaCfg->fwVer, + printk ("megaraid: [%s:%s] detected %d logical drives" CRLFSTR, + megaCfg->fwVer, megaCfg->biosVer, megaCfg->numldrv); + return 0; } @@ -1267,15 +1444,15 @@ } int findCard (Scsi_Host_Template * pHostTmpl, - u_short pciVendor, u_short pciDev, + u16 pciVendor, u16 pciDev, long flag) { mega_host_config *megaCfg; struct Scsi_Host *host; u_char pciBus, pciDevFun, megaIrq; - u_long megaBase; - u_short jdx,pciIdx = 0; - u_short numFound = 0; + u32 megaBase; + u16 pciIdx = 0; + u16 numFound = 0; #if LINUX_VERSION_CODE < 0x20100 while (!pcibios_find_device (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) { @@ -1291,8 +1468,8 @@ pciBus = pdev->bus->number; pciDevFun = pdev->devfn; #endif - if (flag & BOARD_QUARTZ) { - u_short magic; + if ((flag & BOARD_QUARTZ) && (skip_id == -1)) { + u16 magic; pcibios_read_config_word (pciBus, pciDevFun, PCI_CONF_AMISIG, &magic); @@ -1301,7 +1478,7 @@ continue; /* not an AMI board */ } } - printk (KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:fun %d\n", + printk (KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:func %d\n", pciVendor, pciDev, pciIdx, pciBus, @@ -1323,6 +1500,7 @@ pciIdx++; if (flag & BOARD_QUARTZ) { + megaBase &= PCI_BASE_ADDRESS_MEM_MASK; megaBase = (long) ioremap (megaBase, 128); } @@ -1336,7 +1514,7 @@ megaCfg = (mega_host_config *) host->hostdata; memset (megaCfg, 0, sizeof (mega_host_config)); - printk (" scsi%d: Found a MegaRAID controller at 0x%x, IRQ: %d" CRLFSTR, + printk ("scsi%d : Found a MegaRAID controller at 0x%x, IRQ: %d" CRLFSTR, host->host_no, (u_int) megaBase, megaIrq); /* Copy resource info into structure */ @@ -1349,8 +1527,7 @@ megaCfg->host->io_port = megaBase; megaCfg->host->n_io_port = 16; megaCfg->host->unique_id = (pciBus << 8) | pciDevFun; - megaCtlrs[numCtlrs++] = megaCfg; - + megaCtlrs[numCtlrs++] = megaCfg; if (flag != BOARD_QUARTZ) { /* Request our IO Range */ if (check_region (megaBase, 16)) { @@ -1370,14 +1547,9 @@ continue; } - mega_register_mailbox (megaCfg, virt_to_bus ((void *) &megaCfg->mailbox)); + mega_register_mailbox (megaCfg, virt_to_bus ((void *) &megaCfg->mailbox64)); mega_i_query_adapter (megaCfg); - for(jdx=0; jdxnReads[jdx] = 0; - megaCfg->nWrites[jdx] = 0; - } - /* Initialize SCBs */ if (initSCB (megaCfg)) { scsi_unregister (host); @@ -1415,6 +1587,8 @@ skip_id = (skip_id > 15) ? -1 : skip_id; } + printk ("megaraid: " MEGARAID_VERSION CRLFSTR); + count += findCard (pHostTmpl, 0x101E, 0x9010, 0); count += findCard (pHostTmpl, 0x101E, 0x9060, 0); count += findCard (pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ); @@ -1475,16 +1649,15 @@ { static char buffer[512]; mega_host_config *megaCfg; - mega_RAIDINQ *adapterInfo; megaCfg = (mega_host_config *) pSHost->hostdata; - adapterInfo = (mega_RAIDINQ *) megaCfg->mega_buffer; - sprintf (buffer, "AMI MegaRAID %s %d commands %d targs %d chans", + sprintf (buffer, "AMI MegaRAID %s %d commands %d targs %d chans %d luns", megaCfg->fwVer, - adapterInfo->AdpInfo.MaxConcCmds, + megaCfg->productInfo.MaxConcCmds, megaCfg->host->max_id, - megaCfg->host->max_channel); + megaCfg->host->max_channel, + megaCfg->host->max_lun); return buffer; } @@ -1514,9 +1687,13 @@ megaCfg = (mega_host_config *) SCpnt->host->hostdata; if (!(megaCfg->flag & (1L << SCpnt->channel))) { - printk (KERN_INFO "scsi%d: scanning channel %c for devices.\n", + if (SCpnt->channel < SCpnt->host->max_channel) + printk (/*KERN_INFO*/ "scsi%d: scanning channel %c for devices.\n", megaCfg->host->host_no, - SCpnt->channel + 'A'); + SCpnt->channel + '1'); + else + printk(/*KERN_INFO*/ "scsi%d: scanning virtual channel for logical drives.\n", megaCfg->host->host_no); + megaCfg->flag |= (1L << SCpnt->channel); } @@ -1540,6 +1717,7 @@ /* Allocate and build a SCB request */ if ((pScb = mega_build_cmd (megaCfg, SCpnt)) != NULL) { + /*build SCpnt for IOCTL_CMD_NEW cmd in mega_ioctl()*/ /* Add SCB to the head of the pending queue */ ENQUEUE_NL (pScb, mega_scb, megaCfg->qPending, next); @@ -1550,6 +1728,25 @@ else { printk("IRQ pend...\n"); } + + if ( SCpnt->cmnd[0]==IOCTL_CMD_NEW ) + { /* user data from external user buffer */ + char *user_area; + u32 xfer_size; + + pScb->sem=MUTEX_LOCKED; + down(&pScb->sem); + + user_area = *((char **)&pScb->SCpnt->cmnd[4]); + xfer_size = *((u32 *)&pScb->SCpnt->cmnd[8]); + + copy_to_user(user_area,pScb->kern_area,xfer_size); + + kfree(pScb->kern_area); + + freeSCB(megaCfg, pScb); + } + } spin_unlock_irqrestore(&mega_lock,flags); @@ -1750,3 +1947,4 @@ #include "scsi_module.c" #endif + diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/megaraid.h linux/drivers/scsi/megaraid.h --- v2.2.11/linux/drivers/scsi/megaraid.h Mon Aug 9 16:05:56 1999 +++ linux/drivers/scsi/megaraid.h Wed Aug 25 17:29:48 1999 @@ -9,6 +9,7 @@ #define IN_ABORT 0x40000000L #define IN_RESET 0x20000000L #define BOARD_QUARTZ 0x08000000L +#define BOARD_40LD 0x04000000L #define SCB_FREE 0x0 #define SCB_ACTIVE 0x1 @@ -147,32 +148,333 @@ } #endif + +/*********************************************************************** + * Structure Declarations for the Firmware supporting 40 Logical Drives + * and 256 Physical Drives. + ***********************************************************************/ + +#define FC_MAX_LOGICAL_DRIVES 40 +#define FC_MAX_LOG_DEVICES FC_MAX_LOGICAL_DRIVES +#define FC_MAX_SPAN_DEPTH 8 +#define FC_MAX_ROW_SIZE 32 + +#define FC_MAX_CHANNELS 16 +#define FC_MAX_TARGETS_PER_CHANNEL 16 +#define FC_MAX_PHYSICAL_DEVICES 256 + +#define FC_NEW_CONFIG 0xA1 +#define DCMD_FC_CMD 0xA1 + #define NC_SUBOP_PRODUCT_INFO 0x0E + #define NC_SUBOP_ENQUIRY3 0x0F + #define ENQ3_GET_SOLICITED_NOTIFY_ONLY 0x01 + #define ENQ3_GET_SOLICITED_FULL 0x02 + #define ENQ3_GET_UNSOLICITED 0x03 + + +/******************************************** + * PRODUCT_INFO Strucure + ********************************************/ + +#define SIG_40LOG_32STR_8SPN 0x00282008 + +/* + * Utilities declare this strcture size as 1024 bytes. So more fields can + * be added in future. + */ + +struct MRaidProductInfo +{ + u32 DataSize; /* current size in bytes (not including resvd) */ + u32 ConfigSignature; + /* Current value is 0x00282008 + * 0x28=MAX_LOGICAL_DRIVES, + * 0x20=Number of stripes and + * 0x08=Number of spans */ + u8 FwVer[16]; /* printable ASCI string */ + u8 BiosVer[16]; /* printable ASCI string */ + u8 ProductName[80]; /* printable ASCI string */ + + u8 MaxConcCmds; /* Max. concurrent commands supported */ + u8 SCSIChanPresent; /* Number of SCSI Channels detected */ + u8 FCLoopPresent; /* Number of Fibre Loops detected */ + u8 memType; /* EDO, FPM, SDRAM etc */ + + u32 signature; + u16 DramSize; /* In terms of MB */ + u16 subSystemID; + + u16 subSystemVendorID; + u8 numNotifyCounters; + u8 pad1k[889]; /* 135 + 889 resvd = 1024 total size */ +}__attribute__((packed)); +typedef struct MRaidProductInfo megaRaidProductInfo; + +/******************************************** + * Standard ENQUIRY Strucure + ********************************************/ +struct FC_ADP_INFO +{ + u8 MaxConcCmds; /* Max. concurrent commands supported. */ + u8 RbldRate; /* Rebuild Rate. Varies from 0%-100% */ + u8 MaxTargPerChan; /* Max. Targets supported per chan. */ + u8 ChanPresent; /* No. of Chans present on this adapter. */ + u8 FwVer[4]; /* Firmware version. */ + u16 AgeOfFlash; /* No. of times FW has been downloaded. */ + u8 ChipSetValue; /* Contents of 0xC0000832 */ + u8 DramSize; /* In terms of MB */ + u8 CacheFlushInterval; /* In terms of Seconds */ + u8 BiosVersion[4]; + u8 BoardType; + u8 sense_alert; + u8 write_config_count; /* Increase with evry configuration change */ + u8 drive_inserted_count; /* Increase with every drive inserted */ + u8 inserted_drive; /* Channel: Id of inserted drive */ + u8 battery_status; + /* + BIT 0 : battery module missing + BIT 1 : VBAD + BIT 2 : temp high + BIT 3 : battery pack missing + BIT 4,5 : 00 - charge complete + 01 - fast charge in prog + 10 - fast charge fail + 11 - undefined + BIt 6 : counter > 1000 + Bit 7 : undefined + */ + u8 dec_fault_bus_info; /* was resvd */ +}__attribute__((packed)); + +struct FC_LDRV_INFO +{ + u8 NumLDrv; /* No. of Log. Drvs configured. */ + u8 recon_state[FC_MAX_LOGICAL_DRIVES/8]; + /* bit field for State of reconstruct */ + u16 LDrvOpStatus[FC_MAX_LOGICAL_DRIVES/8]; + /* bit field Status of Long Operations. */ + + u32 LDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv. */ + u8 LDrvProp[FC_MAX_LOGICAL_DRIVES]; + u8 LDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives. */ +}__attribute__((packed)); + +#define PREVSTAT_MASK 0xf0 +#define CURRSTAT_MASK 0x0f + +struct FC_PDRV_INFO +{ + u8 PDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys Drvs. */ +}__attribute__((packed)); + + +struct FC_AdapterInq +{ + struct FC_ADP_INFO AdpInfo; + struct FC_LDRV_INFO LogdrvInfo; + struct FC_PDRV_INFO PhysdrvInfo; +}__attribute__((packed)); + + +typedef struct FC_AdapterInq mega_RAIDINQ_FC; + +/******************************************** + * NOTIFICATION Strucure + ********************************************/ + +#define MAX_NOTIFY_SIZE 0x80 +#define CUR_NOTIFY_SIZE sizeof(struct MegaRAID_Notify) + +/* + * Utilities declare this strcture size as ?? bytes. So more fields can + * be added in future. + */ +struct MegaRAID_Notify +{ + u32 globalCounter; /* Any change increments this counter */ + + u8 paramCounter; /* Indicates any params changed */ + u8 paramId; /* Param modified - defined below */ + u16 paramVal; /* New val of last param modified */ + + u8 writeConfigCounter; /* write config occurred */ + u8 writeConfigRsvd[3]; + + u8 ldrvOpCounter; /* Indicates ldrv op started/completed */ + u8 ldrvOpId; /* ldrv num */ + u8 ldrvOpCmd; /* ldrv operation - defined below */ + u8 ldrvOpStatus; /* status of the operation */ + + u8 ldrvStateCounter; /* Indicates change of ldrv state */ + u8 ldrvStateId; /* ldrv num */ + u8 ldrvStateNew; /* New state */ + u8 ldrvStateOld; /* old state */ + + u8 pdrvStateCounter; /* Indicates change of ldrv state */ + u8 pdrvStateId; /* pdrv id */ + u8 pdrvStateNew; /* New state */ + u8 pdrvStateOld; /* old state */ + + u8 pdrvFmtCounter; /* Indicates pdrv format started/over */ + u8 pdrvFmtId; /* pdrv id */ + u8 pdrvFmtVal; /* format started/over */ + u8 pdrvFmtRsvd; + + u8 targXferCounter; /* Indicates SCSI-2 Xfer rate change */ + u8 targXferId; /* pdrv Id */ + u8 targXferVal; /* new Xfer params of last pdrv */ + u8 targXferRsvd; + + u8 fcLoopIdChgCounter; /* Indicates loopid changed */ + u8 fcLoopIdPdrvId; /* pdrv id */ + u8 fcLoopId0; /* loopid on fc loop 0 */ + u8 fcLoopId1; /* loopid on fc loop 1 */ + + u8 fcLoopStateCounter; /* Indicates loop state changed */ + u8 fcLoopState0; /* state of fc loop 0 */ + u8 fcLoopState1; /* state of fc loop 1 */ + u8 fcLoopStateRsvd; +}__attribute__((packed)); + + +/******************************************** + * PARAM IDs in Notify struct + ********************************************/ +#define PARAM_RBLD_RATE 0x01 + /*-------------------------------------- + * Param val = + * byte 0: new rbld rate + *--------------------------------------*/ +#define PARAM_CACHE_FLUSH_INTERVAL 0x02 + /*-------------------------------------- + * Param val = + * byte 0: new cache flush interval + *--------------------------------------*/ +#define PARAM_SENSE_ALERT 0x03 + /*-------------------------------------- + * Param val = + * byte 0: last pdrv id causing chkcond + *--------------------------------------*/ +#define PARAM_DRIVE_INSERTED 0x04 + /*-------------------------------------- + * Param val = + * byte 0: last pdrv id inserted + *--------------------------------------*/ +#define PARAM_BATTERY_STATUS 0x05 + /*-------------------------------------- + * Param val = + * byte 0: battery status + *--------------------------------------*/ + +/******************************************** + * Ldrv operation cmd in Notify struct + ********************************************/ +#define LDRV_CMD_CHKCONSISTANCY 0x01 +#define LDRV_CMD_INITIALIZE 0x02 +#define LDRV_CMD_RECONSTRUCTION 0x03 + +/******************************************** + * Ldrv operation status in Notify struct + ********************************************/ +#define LDRV_OP_SUCCESS 0x00 +#define LDRV_OP_FAILED 0x01 +#define LDRV_OP_ABORTED 0x02 +#define LDRV_OP_CORRECTED 0x03 +#define LDRV_OP_STARTED 0x04 + + +/******************************************** + * Raid Logical drive states. + ********************************************/ +#define RDRV_OFFLINE 0 +#define RDRV_DEGRADED 1 +#define RDRV_OPTIMAL 2 +#define RDRV_DELETED 3 + +/******************************************* + * Physical drive states. + *******************************************/ +#define PDRV_UNCNF 0 +#define PDRV_ONLINE 3 +#define PDRV_FAILED 4 +#define PDRV_RBLD 5 +/* #define PDRV_HOTSPARE 6 */ + +/******************************************* + * Formal val in Notify struct + *******************************************/ +#define PDRV_FMT_START 0x01 +#define PDRV_FMT_OVER 0x02 + +/******************************************** + * FC Loop State in Notify Struct + ********************************************/ +#define ENQ_FCLOOP_FAILED 0 +#define ENQ_FCLOOP_ACTIVE 1 +#define ENQ_FCLOOP_TRANSIENT 2 + +/******************************************** + * ENQUIRY3 Strucure + ********************************************/ +/* + * Utilities declare this strcture size as 1024 bytes. So more fields can + * be added in future. + */ +struct MegaRAID_Enquiry3 +{ + u32 dataSize; /* current size in bytes (not including resvd) */ + + struct MegaRAID_Notify notify; + + u8 notifyRsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE]; + + u8 rbldRate; /* Rebuild rate (0% - 100%) */ + u8 cacheFlushInterval; /* In terms of Seconds */ + u8 senseAlert; + u8 driveInsertedCount; /* drive insertion count */ + + u8 batteryStatus; + u8 numLDrv; /* No. of Log Drives configured */ + u8 reconState[FC_MAX_LOGICAL_DRIVES/8]; /* State of reconstruct */ + u16 lDrvOpStatus[FC_MAX_LOGICAL_DRIVES/8]; /* log. Drv Status */ + + u32 lDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv */ + u8 lDrvProp[FC_MAX_LOGICAL_DRIVES]; + u8 lDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives */ + u8 pDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys. Drvs. */ + u16 physDrvFormat[FC_MAX_PHYSICAL_DEVICES/16]; + + u8 targXfer[80]; /* phys device transfer rate */ + u8 pad1k[263]; /* 761 + 263reserved = 1024 bytes total size */ +}__attribute__((packed)); +typedef struct MegaRAID_Enquiry3 mega_Enquiry3; + /* Structures */ typedef struct _mega_ADP_INFO { - u_char MaxConcCmds; - u_char RbldRate; - u_char MaxTargPerChan; - u_char ChanPresent; - u_char FwVer[4]; - u_short AgeOfFlash; - u_char ChipSet; - u_char DRAMSize; - u_char CacheFlushInterval; - u_char BiosVer[4]; - u_char resvd[7]; + u8 MaxConcCmds; + u8 RbldRate; + u8 MaxTargPerChan; + u8 ChanPresent; + u8 FwVer[4]; + u16 AgeOfFlash; + u8 ChipSetValue; + u8 DramSize; + u8 CacheFlushInterval; + u8 BiosVer[4]; + u8 resvd[7]; } mega_ADP_INFO; typedef struct _mega_LDRV_INFO { - u_char NumLDrv; - u_char resvd[3]; - u_long LDrvSize[MAX_LOGICAL_DRIVES]; - u_char LDrvProp[MAX_LOGICAL_DRIVES]; - u_char LDrvState[MAX_LOGICAL_DRIVES]; + u8 NumLDrv; + u8 resvd[3]; + u32 LDrvSize[MAX_LOGICAL_DRIVES]; + u8 LDrvProp[MAX_LOGICAL_DRIVES]; + u8 LDrvState[MAX_LOGICAL_DRIVES]; } mega_LDRV_INFO; typedef struct _mega_PDRV_INFO { - u_char PDrvState[MAX_PHYSICAL_DRIVES]; - u_char resvd; + u8 PDrvState[MAX_PHYSICAL_DRIVES]; + u8 resvd; } mega_PDRV_INFO; // RAID inquiry: Mailbox command 0x5 @@ -184,65 +486,70 @@ // Passthrough command: Mailbox command 0x3 typedef struct mega_passthru { - u_char timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */ - u_char ars:1; - u_char reserved:3; - u_char islogical:1; - u_char logdrv; /* if islogical == 1 */ - u_char channel; /* if islogical == 0 */ - u_char target; /* if islogical == 0 */ - u_char queuetag; /* unused */ - u_char queueaction; /* unused */ - u_char cdb[MAX_CDB_LEN]; - u_char cdblen; - u_char reqsenselen; - u_char reqsensearea[MAX_REQ_SENSE_LEN]; - u_char numsgelements; - u_char scsistatus; - u_long dataxferaddr; - u_long dataxferlen; + u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */ + u8 ars:1; + u8 reserved:3; + u8 islogical:1; + u8 logdrv; /* if islogical == 1 */ + u8 channel; /* if islogical == 0 */ + u8 target; /* if islogical == 0 */ + u8 queuetag; /* unused */ + u8 queueaction; /* unused */ + u8 cdb[MAX_CDB_LEN]; + u8 cdblen; + u8 reqsenselen; + u8 reqsensearea[MAX_REQ_SENSE_LEN]; + u8 numsgelements; + u8 scsistatus; + u32 dataxferaddr; + u32 dataxferlen; } mega_passthru; typedef struct _mega_mailbox { - /* 0x0 */ u_char cmd; - /* 0x1 */ u_char cmdid; - /* 0x2 */ u_short numsectors; - /* 0x4 */ u_long lba; - /* 0x8 */ u_long xferaddr; - /* 0xC */ u_char logdrv; - /* 0xD */ u_char numsgelements; - /* 0xE */ u_char resvd; - /* 0xF */ u_char busy; - /* 0x10 */ u_char numstatus; - /* 0x11 */ u_char status; - /* 0x12 */ u_char completed[46]; - u_char mraid_poll; - u_char mraid_ack; - u_char pad[16]; + /* 0x0 */ u8 cmd; + /* 0x1 */ u8 cmdid; + /* 0x2 */ u16 numsectors; + /* 0x4 */ u32 lba; + /* 0x8 */ u32 xferaddr; + /* 0xC */ u8 logdrv; + /* 0xD */ u8 numsgelements; + /* 0xE */ u8 resvd; + /* 0xF */ u8 busy; + /* 0x10 */ u8 numstatus; + /* 0x11 */ u8 status; + /* 0x12 */ u8 completed[46]; + u8 mraid_poll; + u8 mraid_ack; + u8 pad[16]; } mega_mailbox; +typedef struct { + u32 xferSegment; /* for 64-bit controllers */ + mega_mailbox mailbox; +} mega_mailbox64; + typedef struct _mega_ioctl_mbox { - /* 0x0 */ u_char cmd; - /* 0x1 */ u_char cmdid; - /* 0x2 */ u_char channel; - /* 0x3 */ u_char param; - /* 0x4 */ u_char pad[4]; - /* 0x8 */ u_long xferaddr; - /* 0xC */ u_char logdrv; - /* 0xD */ u_char numsgelements; - /* 0xE */ u_char resvd; - /* 0xF */ u_char busy; - /* 0x10 */ u_char numstatus; - /* 0x11 */ u_char status; - /* 0x12 */ u_char completed[46]; - u_char mraid_poll; - u_char mraid_ack; - u_char malign[16]; + /* 0x0 */ u8 cmd; + /* 0x1 */ u8 cmdid; + /* 0x2 */ u8 channel; + /* 0x3 */ u8 param; + /* 0x4 */ u8 pad[4]; + /* 0x8 */ u32 xferaddr; + /* 0xC */ u8 logdrv; + /* 0xD */ u8 numsgelements; + /* 0xE */ u8 resvd; + /* 0xF */ u8 busy; + /* 0x10 */ u8 numstatus; + /* 0x11 */ u8 status; + /* 0x12 */ u8 completed[46]; + u8 mraid_poll; + u8 mraid_ack; + u8 malign[16]; } mega_ioctl_mbox; typedef struct _mega_sglist { - u_long address; - u_long length; + u32 address; + u32 length; } mega_sglist; /* Queued command data */ @@ -250,39 +557,51 @@ struct _mega_scb { int idx; - u_long state; - u_long isrcount; - u_char mboxData[16]; + u32 state; + u32 isrcount; + u8 mboxData[16]; mega_passthru pthru; Scsi_Cmnd *SCpnt; mega_sglist *sgList; + char *kern_area; /* Only used for large ioctl xfers */ + struct wait_queue *ioctl_wait; + struct semaphore sem; mega_scb *next; }; /* Per-controller data */ typedef struct _mega_host_config { - u_char numldrv; - u_long flag; - u_long base; + u8 numldrv; + u32 flag; + u32 base; mega_scb *qFree; mega_scb *qPending; - u_long nReads[MAX_LOGICAL_DRIVES]; - u_long nWrites[MAX_LOGICAL_DRIVES]; + u32 nReads[FC_MAX_LOGICAL_DRIVES]; + u32 nWrites[FC_MAX_LOGICAL_DRIVES]; /* Host adapter parameters */ - u_char fwVer[7]; - u_char biosVer[7]; + u8 fwVer[7]; + u8 biosVer[7]; struct Scsi_Host *host; - /* The following must be DMA-able!! */ - volatile mega_mailbox *mbox; - volatile mega_mailbox mailbox; - volatile u_char mega_buffer[2 * 1024L]; + volatile mega_mailbox64 *mbox64; /* ptr to beginning of 64-bit mailbox */ + volatile mega_mailbox *mbox; /* ptr to beginning of standard mailbox */ + volatile mega_mailbox64 mailbox64; +#if 0 + volatile union { + u8 generic_buffer[2 * 1024L]; + mega_RAIDINQ adapterInfoData; + mega_Enquiry3 enquiry3Data; + }mega_buffer; +#else + volatile u8 mega_buffer[2*1024L]; +#endif + volatile megaRaidProductInfo productInfo; - u_char max_cmds; + u8 max_cmds; mega_scb scbList[MAX_COMMANDS]; } mega_host_config; diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/mesh.c linux/drivers/scsi/mesh.c --- v2.2.11/linux/drivers/scsi/mesh.c Wed Dec 30 10:55:07 1998 +++ linux/drivers/scsi/mesh.c Wed Aug 25 17:29:48 1999 @@ -252,8 +252,10 @@ continue; } mesh_host->unique_id = nmeshes; +#ifndef MODULE note_scsi_host(mesh, mesh_host); - +#endif + ms = (struct mesh_state *) mesh_host->hostdata; if (ms == 0) panic("no mesh state"); @@ -685,16 +687,15 @@ unsigned long flags; for (;;) { - save_flags(flags); - cli(); + spin_lock_irqsave(&io_request_lock, flags); cmd = ms->completed_q; if (cmd == NULL) { - restore_flags(flags); + spin_unlock_irqrestore(&io_request_lock, flags); break; } ms->completed_q = (Scsi_Cmnd *) cmd->host_scribble; - restore_flags(flags); (*cmd->scsi_done)(cmd); + spin_unlock_irqrestore(&io_request_lock, flags); } } @@ -762,8 +763,8 @@ Scsi_Cmnd *cmd = ms->current_req; struct mesh_target *tp = &ms->tgts[ms->conn_tgt]; - dlog(ms, "start_phase err/exc/fc/seq = %.8x", - MKWORD(mr->error, mr->exception, mr->fifo_count, mr->sequence)); + dlog(ms, "start_phase nmo/exc/fc/seq = %.8x", + MKWORD(ms->n_msgout, mr->exception, mr->fifo_count, mr->sequence)); out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0); switch (ms->msgphase) { @@ -1057,6 +1058,7 @@ t = 230; /* wait up to 230us */ while ((mr->bus_status0 & BS0_REQ) == 0) { if (--t < 0) { + dlog(ms, "impatient for req", ms->n_msgout); ms->msgphase = msg_none; break; } @@ -1643,7 +1645,7 @@ static void mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd) { -#if 0 +#if 1 if (ms->completed_q == NULL) ms->completed_q = cmd; else diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/pas16.c linux/drivers/scsi/pas16.c --- v2.2.11/linux/drivers/scsi/pas16.c Sat Apr 11 11:13:25 1998 +++ linux/drivers/scsi/pas16.c Wed Aug 25 17:29:48 1999 @@ -73,7 +73,6 @@ * software after reset using the default_irq for the * current board number. * - * * 2. With command line overrides - pas16=port,irq may be * used on the LILO command line to override the defaults. * @@ -83,6 +82,11 @@ * -DPAS16_OVERRIDE={{0x388, 10}} * NOTE: Untested. * + * 4. When included as a module, with arguments passed on the command line: + * pas16_irq=xx the interrupt + * pas16_addr=xx the port + * e.g. "modprobe pas16 pas16_addr=0x388 pas16_irq=5" + * * Note that if the override methods are used, place holders must * be specified for other boards in the system. * @@ -99,7 +103,11 @@ * interrupts. Ie, for a board at the default 0x388 base port, * boot: linux pas16=0x388,255 * - * (255 is the IRQ_NONE constant in NCR5380.h) + * IRQ_NONE (255) should be specified for no interrupt, + * IRQ_AUTO (254) to autoprobe for an IRQ line if overridden + * on the command line. + * + * (IRQ_AUTO == 254, IRQ_NONE == 255 in NCR5380.h) */ #ifdef MODULE @@ -129,6 +137,8 @@ }; static int pas_maxi = 0; static int pas_wmaxi = 0; +static unsigned short pas16_addr = 0; +static int pas16_irq = 0; int scsi_irq_translate[] = @@ -383,6 +393,22 @@ tpnt->proc_dir = &proc_scsi_pas16; tpnt->proc_info = &pas16_proc_info; + if (pas16_addr != 0) { + overrides[0].io_port = pas16_addr; + /* + * This is how we avoid seeing more than + * one host adapter at the same I/O port. + * Cribbed shamelessly from pas16_setup(). + */ + for (count = 0; count < NO_BASES; ++count) + if (bases[count].io_port == pas16_addr) { + bases[count].noauto = 1; + break; + } + } + if (pas16_irq != 0) + overrides[0].irq = pas16_irq; + for (count = 0; current_override < NO_OVERRIDES; ++current_override) { io_port = 0; @@ -576,7 +602,11 @@ #ifdef MODULE /* Eventually this will go into an include file, but this will be later */ Scsi_Host_Template driver_template = MV_PAS16; - + #include + +MODULE_PARM(pas16_addr, "h"); +MODULE_PARM(pas16_irq, "i"); + #include "scsi_module.c" #endif diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/sym53c8xx_defs.h linux/drivers/scsi/sym53c8xx_defs.h --- v2.2.11/linux/drivers/scsi/sym53c8xx_defs.h Mon Aug 9 16:05:57 1999 +++ linux/drivers/scsi/sym53c8xx_defs.h Wed Aug 25 17:29:48 1999 @@ -473,6 +473,9 @@ {PCI_DEVICE_ID_NCR_53C875, 0x2f, "875E", 6, 16, 5, \ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ , \ + {PCI_DEVICE_ID_NCR_53C875, 0xff, "876", 6, 16, 5, \ + FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ + , \ {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5, \ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\ , \ diff -u --recursive --new-file v2.2.11/linux/drivers/scsi/wd33c93.h linux/drivers/scsi/wd33c93.h --- v2.2.11/linux/drivers/scsi/wd33c93.h Mon Aug 9 16:05:57 1999 +++ linux/drivers/scsi/wd33c93.h Wed Aug 25 17:29:48 1999 @@ -23,6 +23,7 @@ #ifndef WD33C93_H #define WD33C93_H +#include #define PROC_INTERFACE /* add code for /proc/scsi/wd33c93/xxx interface */ #ifdef PROC_INTERFACE diff -u --recursive --new-file v2.2.11/linux/drivers/sound/Config.in linux/drivers/sound/Config.in --- v2.2.11/linux/drivers/sound/Config.in Sun Mar 7 15:22:06 1999 +++ linux/drivers/sound/Config.in Wed Aug 25 17:29:48 1999 @@ -9,6 +9,9 @@ # Prompt user for primary drivers. +if [ "$CONFIG_VISWS" = "y" ]; then + dep_tristate 'SGI Visual Workstation Sound' CONFIG_SOUND_VWSND $CONFIG_SOUND +fi dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND if [ "$CONFIG_SOUND_ES1370" = "y" ]; then bool 'Joystick support at boot time' CONFIG_SOUND_ES1370_JOYPORT_BOOT @@ -20,6 +23,10 @@ hex 'Gameport I/O 200,208,210,218' CONFIG_SOUND_ES1371_GAMEPORT 200 fi fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'ESS Solo1 (Experimental)' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND +fi + dep_tristate 'S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND dep_tristate 'Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND diff -u --recursive --new-file v2.2.11/linux/drivers/sound/Makefile linux/drivers/sound/Makefile --- v2.2.11/linux/drivers/sound/Makefile Thu Jan 14 22:59:47 1999 +++ linux/drivers/sound/Makefile Wed Aug 25 17:29:48 1999 @@ -72,12 +72,14 @@ obj-$(CONFIG_SOUND_VMIDI) += v_midi.o obj-$(CONFIG_SOUND_YM3812) += adlib_card.o opl3.o obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o +obj-$(CONFIG_SOUND_VWSND) += vwsnd.o obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o #jnx obj-$(CONFIG_SOUND_ES1370) += es1370.o obj-$(CONFIG_SOUND_ES1371) += es1371.o +obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o # Declare multi-part drivers. diff -u --recursive --new-file v2.2.11/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c --- v2.2.11/linux/drivers/sound/dmasound.c Mon Aug 9 16:05:57 1999 +++ linux/drivers/sound/dmasound.c Wed Aug 25 17:29:48 1999 @@ -242,6 +242,9 @@ static short *beep_buf; static volatile struct dbdma_cmd *beep_dbdma_cmd; static void (*orig_mksound)(unsigned int, unsigned int); +static int is_pbook_3400; +static int is_pbook_G3; +static unsigned char *macio_base; /* Burgundy functions */ static void awacs_burgundy_wcw(unsigned addr,unsigned newval); @@ -255,9 +258,9 @@ /* * Stuff for restoring after a sleep. */ -static int awacs_sleep_notify(struct notifier_block *, unsigned long, void *); -struct notifier_block awacs_sleep_notifier = { - awacs_sleep_notify +static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when); +struct pmu_sleep_notifier awacs_sleep_notifier = { + awacs_sleep_notify, SLEEP_LEVEL_SOUND, }; #endif /* CONFIG_PMAC_PBOOK */ @@ -3020,7 +3023,7 @@ kfree(beep_buf); kd_mksound = orig_mksound; #ifdef CONFIG_PMAC_PBOOK - notifier_chain_unregister(&sleep_notifier_list, &awacs_sleep_notifier); + pmu_unregister_sleep_notifier(&awacs_sleep_notifier); #endif } #endif /* MODULE */ @@ -3342,14 +3345,15 @@ /* * Save state when going to sleep, restore it afterwards. */ -static int awacs_sleep_notify(struct notifier_block *this, - unsigned long code, void *x) +static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) { - switch (code) { - case PBOOK_SLEEP: + switch (when) { + case PBOOK_SLEEP_NOW: /* XXX we should stop any dma in progress when going to sleep and restart it when we wake. */ PMacSilence(); + disable_irq(awacs_irq); + disable_irq(awacs_tx_irq); break; case PBOOK_WAKE: out_le32(&awacs->control, MASK_IEPC @@ -3360,8 +3364,10 @@ awacs_write(awacs_reg[2] | MASK_ADDR2); awacs_write(awacs_reg[4] | MASK_ADDR4); out_le32(&awacs->byteswap, sound.hard.format != AFMT_S16_BE); + enable_irq(awacs_irq); + enable_irq(awacs_tx_irq); } - return NOTIFY_DONE; + return PBOOK_SLEEP_OK; } #endif /* CONFIG_PMAC_PBOOK */ @@ -3983,7 +3989,8 @@ #ifdef CONFIG_PPC case DMASND_AWACS: - if (awacs_revisionn_addrs > 0) { + is_pbook_G3 = 1; + macio_base = (unsigned char *) + ioremap(np->addrs[0].address, 0x40); + /* enable CD sound input */ + out_8(macio_base + 0x37, 3); + } + } } #endif /* CONFIG_PPC */ diff -u --recursive --new-file v2.2.11/linux/drivers/sound/esssolo1.c linux/drivers/sound/esssolo1.c --- v2.2.11/linux/drivers/sound/esssolo1.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/esssolo1.c Wed Aug 25 17:29:48 1999 @@ -0,0 +1,2226 @@ +/*****************************************************************************/ + +/* + * esssolo1.c -- ESS Technology Solo1 (ES1946) audio driver. + * + * Copyright (C) 1998-1999 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. + * + * Module command line parameters: + * none so far + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/midi simple MIDI UART interface, no ioctl + * + * Revision history + * 10.11.98 0.1 Initial release (without any hardware) + * 22.03.99 0.2 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed + * 07.04.99 0.3 implemented the following ioctl's: SOUND_PCM_READ_RATE, + * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; + * Alpha fixes reported by Peter Jones + * 15.06.99 0.4 Fix bad allocation bug. + * Thanks to Deti Fliegl + * 28.06.99 0.5 Add pci_set_master + * 12.08.99 0.6 Fix MIDI UART crashing the driver + * Changed mixer semantics from OSS documented + * behaviour to OSS "code behaviour". + * Recording might actually work now. + * The real DDMA controller address register is at PCI config + * 0x60, while the register at 0x18 is used as a placeholder + * register for BIOS address allocation. This register + * is supposed to be copied into 0x60, according + * to the Solo1 datasheet. When I do that, I can access + * the DDMA registers except the mask bit, which + * is stuck at 1. When I copy the contents of 0x18 +0x10 + * to the DDMA base register, everything seems to work. + * The fun part is that the Windows Solo1 driver doesn't + * seem to do these tricks. + * Bugs remaining: plops and clicks when starting/stopping playback + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ESS +#define PCI_VENDOR_ID_ESS 0x125d +#endif +#ifndef PCI_DEVICE_ID_ESS_SOLO1 +#define PCI_DEVICE_ID_ESS_SOLO1 0x1969 +#endif + +#define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) + +#define DDMABASE_OFFSET 0x10 /* chip bug workaround kludge */ +#define DDMABASE_EXTENT 16 + +#define IOBASE_EXTENT 16 +#define SBBASE_EXTENT 16 +#define VCBASE_EXTENT (DDMABASE_EXTENT+DDMABASE_OFFSET) +#define MPUBASE_EXTENT 4 +#define GPBASE_EXTENT 4 + + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +/* --------------------------------------------------------------------- */ + +#include + +#if LINUX_VERSION_CODE < 0x02030d +#ifdef MODULE +#define __exit +#define module_exit(x) void cleanup_module(void) { x(); } +#define module_init(x) int init_module(void) { return x(); } +#else +#define __exit __attribute__ ((unused, __section__ (".text.init"))) +#define module_exit(x) /* nothing */ +#define module_init(x) /* nothing */ +#endif + +#define DECLARE_WAIT_QUEUE_HEAD(w) struct wait_queue *w = NULL +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = {(c), NULL} +#define wait_queue_head_t struct wait_queue * +#define init_waitqueue_head(w) *(w) = 0 +#define init_MUTEX(m) *(m) = MUTEX +#endif + +/* --------------------------------------------------------------------- */ + +struct solo1_state { + /* magic */ + unsigned int magic; + + /* we keep the cards in a linked list */ + struct solo1_state *next; + + /* pcidev is needed to turn off the DDMA controller at driver shutdown */ + struct pci_dev *pcidev; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned long iobase, sbbase, vcbase, ddmabase, mpubase, gpbase; /* long for SPARC */ + unsigned int irq; + + /* mixer registers */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + /* wave stuff */ + unsigned fmt; + unsigned channels; + unsigned rate; + unsigned char clkdiv; + unsigned ena; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; +}; + +/* --------------------------------------------------------------------- */ + +struct solo1_state *devs = NULL; + +/* --------------------------------------------------------------------- */ + +extern inline void write_seq(struct solo1_state *s, unsigned char data) +{ + int i; + unsigned long flags; + + /* the __cli stunt is to send the data within the command window */ + for (i = 0; i < 0xffff; i++) { + __save_flags(flags); + __cli(); + if (!(inb(s->sbbase+0xc) & 0x80)) { + outb(data, s->sbbase+0xc); + __restore_flags(flags); + return; + } + __restore_flags(flags); + } +} + +extern inline int read_seq(struct solo1_state *s, unsigned char *data) +{ + int i; + + if (!data) + return 0; + for (i = 0; i < 0xffff; i++) + if (inb(s->sbbase+0xe) & 0x80) { + *data = inb(s->sbbase+0xa); + return 1; + } + return 0; +} + +static int inline reset_ctrl(struct solo1_state *s) +{ + int i; + + outb(3, s->sbbase+6); /* clear sequencer and FIFO */ + udelay(10); + outb(0, s->sbbase+6); + for (i = 0; i < 0xffff; i++) + if (inb(s->sbbase+0xe) & 0x80) + if (inb(s->sbbase+0xa) == 0xaa) { + write_seq(s, 0xc6); /* enter enhanced mode */ + return 1; + } + return 0; +} + +static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char data) +{ + write_seq(s, reg); + write_seq(s, data); +} + +static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg) +{ + unsigned char r; + + write_seq(s, 0xc0); + write_seq(s, reg); + read_seq(s, &r); + return r; +} + +static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data) +{ + outb(reg, s->sbbase+4); + outb(data, s->sbbase+5); +} + +static unsigned char read_mixer(struct solo1_state *s, unsigned char reg) +{ + outb(reg, s->sbbase+4); + return inb(s->sbbase+5); +} + +/* --------------------------------------------------------------------- */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +extern inline void stop_dac(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; + write_mixer(s, 0x78, 0x10); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + s->ena |= FMODE_WRITE; + write_mixer(s, 0x78, 0x12); + udelay(10); + write_mixer(s, 0x78, 0x13); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_adc(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + write_ctrl(s, 0xb8, 0xe); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct solo1_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_READ) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) + && s->dma_adc.ready) { + s->ena |= FMODE_READ; + write_ctrl(s, 0xb8, 0xf); +#if 0 + printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf); + printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x stat: 0x%02x\n", + inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8)); +#endif + outb(0, s->ddmabase+0xd); /* master reset */ + outb(1, s->ddmabase+0xf); /* mask */ + outb(0x54/*0x14*/, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + outb(0, s->ddmabase+0xf); + } + spin_unlock_irqrestore(&s->lock, flags); +#if 0 + printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x SBstat: 0x%02x\n" + KERN_DEBUG "solo1: DMA: stat: 0x%02x cnt: 0x%04x mask: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->sbbase+0xc), + inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf)); + printk(KERN_DEBUG "solo1: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1: B1: 0x%02x B2: 0x%02x B4: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb4), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), + read_ctrl(s, 0xb9)); +#endif +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +extern inline void dealloc_dmabuf(struct dmabuf *db) +{ + unsigned long map, mapend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db, int gfp_mask) +{ + int order; + unsigned bytespersec; + unsigned bufs, sample_shift = 0; + unsigned long map, mapend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(gfp_mask, order))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + } + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->channels > 1) + sample_shift++; + bytespersec = s->rate << sample_shift; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytespersec) + db->fragshift = ld2(bytespersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytespersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift; + db->dmasize = db->numfrag << db->fragshift; + return 0; +} + +extern inline int prog_dmabuf_adc(struct solo1_state *s) +{ + unsigned long va; + int c; + + stop_adc(s); + if ((c = prog_dmabuf(s, &s->dma_adc, GFP_KERNEL | GFP_DMA))) + return c; + va = virt_to_bus(s->dma_adc.rawbuf); + if ((va & ~((1<<24)-1))) + panic("solo1: buffer above 16M boundary"); + outb(0, s->ddmabase+0xd); /* clear */ + outb(1, s->ddmabase+0xf); /* mask */ + /*outb(0, s->ddmabase+8);*/ /* enable (enable is active low!) */ + outb(0x54, s->ddmabase+0xb); /* DMA_MODE_READ | DMA_MODE_AUTOINIT */ + outl(va, s->ddmabase); + outw(s->dma_adc.dmasize-1, s->ddmabase+4); + c = - s->dma_adc.fragsamples; + write_ctrl(s, 0xa4, c); + write_ctrl(s, 0xa5, c >> 8); + outb(0, s->ddmabase+0xf); + s->dma_adc.ready = 1; + return 0; +} + +extern inline int prog_dmabuf_dac(struct solo1_state *s) +{ + unsigned long va; + int c; + + stop_dac(s); + if ((c = prog_dmabuf(s, &s->dma_dac, GFP_KERNEL))) + return c; + memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */ + va = virt_to_bus(s->dma_dac.rawbuf); + if ((va ^ (va + s->dma_dac.dmasize - 1)) & ~((1<<20)-1)) + panic("solo1: buffer crosses 1M boundary"); + outl(va, s->iobase); + /* warning: s->dma_dac.dmasize & 0xffff must not be zero! i.e. this limits us to a 32k buffer */ + outw(s->dma_dac.dmasize, s->iobase+4); + c = - s->dma_dac.fragsamples; + write_mixer(s, 0x74, c); + write_mixer(s, 0x76, c >> 8); + outb(0xa, s->iobase+6); + s->dma_dac.ready = 1; + return 0; +} + +extern inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ + +static void solo1_update_ptr(struct solo1_state *s) +{ + int diff; + unsigned hwptr; + + /* update ADC pointer */ + if (s->ena & FMODE_READ) { + hwptr = (s->dma_adc.dmasize - 1 - inw(s->ddmabase+4)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; +#if 0 + printk(KERN_DEBUG "solo1: rd: hwptr %u swptr %u dmasize %u count %u\n", + s->dma_adc.hwptr, s->dma_adc.swptr, s->dma_adc.dmasize, s->dma_adc.count); +#endif + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->ena &= ~FMODE_READ; + write_ctrl(s, 0xb8, 0xe); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC pointer */ + if (s->ena & FMODE_WRITE) { + hwptr = (s->dma_dac.dmasize - inw(s->iobase+4)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; +#if 0 + printk(KERN_DEBUG "solo1: wr: hwptr %u swptr %u dmasize %u count %u\n", + s->dma_dac.hwptr, s->dma_dac.swptr, s->dma_dac.dmasize, s->dma_dac.count); +#endif + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->ena &= ~FMODE_WRITE; + write_mixer(s, 0x78, 0x12); + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, + s->dma_dac.fragsize, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count < (signed)s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* --------------------------------------------------------------------- */ + +static void prog_codec(struct solo1_state *s) +{ + unsigned long flags; + int fdiv, filter; + unsigned char c; + + reset_ctrl(s); + write_seq(s, 0xd3); + /* program sampling rates */ + filter = s->rate * 9 / 20; /* Set filter roll-off to 90% of rate/2 */ + fdiv = 256 - 7160000 / (filter * 82); + spin_lock_irqsave(&s->lock, flags); + write_ctrl(s, 0xa1, s->clkdiv); + write_ctrl(s, 0xa2, fdiv); + write_mixer(s, 0x70, s->clkdiv); + write_mixer(s, 0x72, fdiv); + /* program ADC parameters */ + write_ctrl(s, 0xb8, 0xe); + write_ctrl(s, 0xb9, /*0x1*/0); + write_ctrl(s, 0xa8, (s->channels > 1) ? 0x11 : 0x12); + c = 0xd0; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + c |= 0x04; + if (s->fmt & (AFMT_S16_LE | AFMT_S8)) + c |= 0x20; + if (s->channels > 1) + c ^= 0x48; + write_ctrl(s, 0xb7, (c & 0x70) | 1); + write_ctrl(s, 0xb7, c); + write_ctrl(s, 0xb1, 0x50); + write_ctrl(s, 0xb2, 0x50); + /* program DAC parameters */ + c = 0x40; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + c |= 1; + if (s->fmt & (AFMT_S16_LE | AFMT_S8)) + c |= 4; + if (s->channels > 1) + c |= 2; + write_mixer(s, 0x7a, c); + write_mixer(s, 0x78, 0x10); + s->ena = 0; + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "solo1: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SOLO1_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static int mixer_ioctl(struct solo1_state *s, unsigned int cmd, unsigned long arg) +{ + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_VOLUME, + SOUND_MASK_MIC, 0, SOUND_MASK_LINE, 0 + }; + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, /* voice */ + [SOUND_MIXER_SYNTH] = 2, /* FM */ + [SOUND_MIXER_CD] = 3, /* CD */ + [SOUND_MIXER_LINE] = 4, /* Line */ + [SOUND_MIXER_LINE1] = 5, /* AUX */ + [SOUND_MIXER_MIC] = 6, /* Mic */ + [SOUND_MIXER_LINE2] = 7, /* Mono in */ + [SOUND_MIXER_SPEAKER] = 8, /* Speaker */ + [SOUND_MIXER_RECLEV] = 9, /* Recording level */ + [SOUND_MIXER_VOLUME] = 10 /* Master Volume */ + }; + static const unsigned char mixreg[] = { + 0x7c, /* voice */ + 0x36, /* FM */ + 0x38, /* CD */ + 0x3e, /* Line */ + 0x3a, /* AUX */ + 0x1a, /* Mic */ + 0x6d /* Mono in */ + }; + unsigned char l, r, rl, rr, vidx; + int i, val; + + VALIDATE_STATE(s); + + if (cmd == SOUND_MIXER_PRIVATE1) { + /* enable/disable/query mixer preamp */ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != -1) { + val = val ? 0xff : 0xf7; + write_mixer(s, 0x7d, (read_mixer(s, 0x7d) | 0x08) & val); + } + val = (read_mixer(s, 0x7d) & 0x08) ? 1 : 0; + return put_user(val, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + /* enable/disable/query spatializer */ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != -1) { + val &= 0x3f; + write_mixer(s, 0x52, val); + write_mixer(s, 0x50, val ? 0x08 : 0); + } + return put_user(read_mixer(s, 0x52), (int *)arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "Solo1", sizeof(info.id)); + strncpy(info.name, "ESS Solo1", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_src[read_mixer(s, 0x1c) & 7], (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV | + SOUND_MASK_SPEAKER, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | SOUND_MASK_CD | + SOUND_MASK_LINE | SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | SOUND_MASK_LINE2 | SOUND_MASK_RECLEV, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + return put_user(s->mix.vol[vidx-1], (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ +#if 0 + { + static const unsigned char regs[] = { + 0x1c, 0x1a, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x60, 0x62, 0x6d, 0x7c + }; + int i; + + for (i = 0; i < sizeof(regs); i++) + printk(KERN_DEBUG "solo1: mixer reg 0x%02x: 0x%02x\n", + regs[i], read_mixer(s, regs[i])); + printk(KERN_DEBUG "solo1: ctrl reg 0x%02x: 0x%02x\n", + 0xb4, read_ctrl(s, 0xb4)); + } +#endif + get_user_ret(val, (int *)arg, -EFAULT); + i = hweight32(val); + if (i == 0) + return 0; + else if (i > 1) + val &= ~mixer_src[read_mixer(s, 0x1c) & 7]; + for (i = 0; i < 8; i++) { + if (mixer_src[i] & val) + break; + } + if (i > 7) + return 0; + write_mixer(s, 0x1c, i); + return 0; + + case SOUND_MIXER_VOLUME: + get_user_ret(val, (int *)arg, -EFAULT); + l = val & 0xff; + if (l > 100) + l = 100; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (l < 6) { + rl = 0x40; + l = 0; + } else { + rl = (l * 2 - 11) / 3; + l = (rl * 3 + 11) / 2; + } + if (r < 6) { + rr = 0x40; + r = 0; + } else { + rr = (r * 2 - 11) / 3; + r = (rr * 3 + 11) / 2; + } + write_mixer(s, 0x60, rl); + write_mixer(s, 0x62, rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[9] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[9] = val; +#endif + return put_user(s->mix.vol[9], (int *)arg); + + case SOUND_MIXER_SPEAKER: + get_user_ret(val, (int *)arg, -EFAULT); + l = val & 0xff; + if (l > 100) + l = 100; + else if (l < 2) + l = 2; + rl = (l - 2) / 14; + l = rl * 14 + 2; + write_mixer(s, 0x3c, rl); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = l * 0x101; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int *)arg); + + case SOUND_MIXER_RECLEV: + get_user_ret(val, (int *)arg, -EFAULT); + l = (val << 1) & 0x1fe; + if (l > 200) + l = 200; + else if (l < 5) + l = 5; + r = (val >> 7) & 0x1fe; + if (r > 200) + r = 200; + else if (r < 5) + r = 5; + rl = (l - 5) / 13; + rr = (r - 5) / 13; + r = (rl * 13 + 5) / 2; + l = (rr * 13 + 5) / 2; + write_ctrl(s, 0xb4, (rl << 4) | rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + l = (val << 1) & 0x1fe; + if (l > 200) + l = 200; + else if (l < 5) + l = 5; + r = (val >> 7) & 0x1fe; + if (r > 200) + r = 200; + else if (r < 5) + r = 5; + rl = (l - 5) / 13; + rr = (r - 5) / 13; + r = (rl * 13 + 5) / 2; + l = (rr * 13 + 5) / 2; + write_mixer(s, mixreg[vidx-1], (rl << 4) | rr); +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx-1] = ((unsigned int)r << 8) | l; +#else + s->mix.vol[vidx-1] = val; +#endif + return put_user(s->mix.vol[vidx-1], (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +static loff_t solo1_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int solo1_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct solo1_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + return 0; +} + +static int solo1_release_mixdev(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + +static int solo1_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct solo1_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations solo1_mixer_fops = { + &solo1_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &solo1_ioctl_mixdev, + NULL, /* mmap */ + &solo1_open_mixdev, + NULL, /* flush */ + &solo1_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct solo1_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_dac.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; + if (s->fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "solo1: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t solo1_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc), cnt); +#endif + if (cnt <= 0) { + start_adc(s); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" + KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" + KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); +#endif + if (inb(s->ddmabase+15) & 1) + printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_adc.wait); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: regs: A1: 0x%02x A2: 0x%02x A4: 0x%02x A5: 0x%02x A8: 0x%02x\n" + KERN_DEBUG "solo1_read: regs: B1: 0x%02x B2: 0x%02x B7: 0x%02x B8: 0x%02x B9: 0x%02x\n" + KERN_DEBUG "solo1_read: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x mask: 0x%02x\n" + KERN_DEBUG "solo1_read: SBstat: 0x%02x cnt: %u\n", + read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), + read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), read_ctrl(s, 0xb9), + inl(s->ddmabase), inw(s->ddmabase+4), inb(s->ddmabase+8), inb(s->ddmabase+15), inb(s->sbbase+0xc), cnt); +#endif + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); +#ifdef DEBUGREC + printk(KERN_DEBUG "solo1_read: reg B8: 0x%02x DMAstat: 0x%02x DMAcnt: 0x%04x SBstat: 0x%02x\n", + read_ctrl(s, 0xb8), inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->sbbase+0xc)); +#endif + } + return ret; +} + +static ssize_t solo1_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; +#if 0 + printk(KERN_DEBUG "solo1_write: reg 70: 0x%02x 71: 0x%02x 72: 0x%02x 74: 0x%02x 76: 0x%02x 78: 0x%02x 7A: 0x%02x\n" + KERN_DEBUG "solo1_write: DMA: addr: 0x%08x cnt: 0x%04x stat: 0x%02x SBstat: 0x%02x\n", + read_mixer(s, 0x70), read_mixer(s, 0x71), read_mixer(s, 0x72), read_mixer(s, 0x74), read_mixer(s, 0x76), + read_mixer(s, 0x78), read_mixer(s, 0x7a), inl(s->iobase), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); + printk(KERN_DEBUG "solo1_write: reg 78: 0x%02x reg 7A: 0x%02x DMAcnt: 0x%04x DMAstat: 0x%02x SBstat: 0x%02x\n", + read_mixer(s, 0x78), read_mixer(s, 0x7a), inw(s->iobase+4), inb(s->iobase+6), inb(s->sbbase+0xc)); +#endif + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_dac.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } + return ret; +} + +static unsigned int solo1_poll(struct file *file, struct poll_table_struct *wait) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize > s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + + +static int solo1_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + return 0; +} + +static int solo1_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + int div1, div2; + unsigned rate1, rate2; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + prog_codec(s); + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program sampling rates */ + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + div1 = (768000 + val / 2) / val; + rate1 = (768000 + div1 / 2) / div1; + div1 = -div1; + div2 = (793800 + val / 2) / val; + rate2 = (793800 + div2 / 2) / div2; + div2 = (-div2) & 0x7f; + if (abs(val - rate2) < abs(val - rate1)) { + rate1 = rate2; + div1 = div2; + } + s->rate = rate1; + s->clkdiv = div1; + prog_codec(s); + } + return put_user(s->rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program channels */ + s->channels = val ? 2 : 1; + prog_codec(s); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program channels */ + s->channels = val ? 2 : 1; + prog_codec(s); + } + return put_user(s->channels, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + /* program format */ + if (val != AFMT_S16_LE && val != AFMT_U16_LE && + val != AFMT_S8 && val != AFMT_U8) + val = AFMT_U8; + s->fmt = val; + prog_codec(s); + } + return put_user(s->fmt, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & s->ena & FMODE_READ) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & s->ena & FMODE_WRITE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + if (inb(s->ddmabase+15) & 1) + printk(KERN_ERR "solo1: cannot start recording, DDMA mask bit stuck at 1\n"); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->ena & FMODE_WRITE) && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->ena & FMODE_READ) && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + solo1_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(s->rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user(s->channels, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & (AFMT_S8|AFMT_U8)) ? 8 : 16, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int solo1_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + outb(0, s->iobase+6); /* disable DMA */ + dealloc_dmabuf(&s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + outb(1, s->ddmabase+0xf); /* mask DMA channel */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= ~(FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static int solo1_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct solo1_state *s = devs; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (FMODE_READ | FMODE_WRITE)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->fmt = AFMT_U8; + s->channels = 1; + s->rate = 8000; + s->clkdiv = 96 | 0x80; + s->ena = 0; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + if (prog_dmabuf_dac(s) || prog_dmabuf_adc(s)) { + solo1_release(inode, file); + return -ENOMEM; + } + prog_codec(s); + return 0; +} + +static /*const*/ struct file_operations solo1_audio_fops = { + &solo1_llseek, + &solo1_read, + &solo1_write, + NULL, /* readdir */ + &solo1_poll, + &solo1_ioctl, + &solo1_mmap, + &solo1_open, + NULL, /* flush */ + &solo1_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +/* hold spinlock for the following! */ +static void solo1_handle_midi(struct solo1_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->mpubase)) + return; + wake = 0; + while (!(inb(s->mpubase+1) & 0x80)) { + ch = inb(s->mpubase); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->mpubase+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->mpubase); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static void solo1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct solo1_state *s = (struct solo1_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->iobase+7); /* get interrupt source(s) */ + if (!intsrc) + return; + (void)inb(s->sbbase+0xe); /* clear interrupt */ + spin_lock(&s->lock); + /* clear audio interrupts first */ + if (intsrc & 0x20) + write_mixer(s, 0x7a, read_mixer(s, 0x7a) & 0x7f); + solo1_update_ptr(s); + solo1_handle_midi(s); + spin_unlock(&s->lock); +} + +static void solo1_midi_timer(unsigned long data) +{ + struct solo1_state *s = (struct solo1_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + +static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + solo1_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +static unsigned int solo1_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int solo1_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct solo1_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(0xff, s->mpubase+1); /* reset command */ + outb(0x3f, s->mpubase+1); /* uart command */ + if (!(inb(s->mpubase+1) & 0x80)) + inb(s->mpubase); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + outb(0xb0, s->iobase + 7); /* enable A1, A2, MPU irq's */ + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = solo1_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int solo1_midi_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG "solo1: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */ + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations solo1_midi_fops = { + &solo1_llseek, + &solo1_midi_read, + &solo1_midi_write, + NULL, /* readdir */ + &solo1_midi_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + &solo1_midi_open, + NULL, /* flush */ + &solo1_midi_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static int solo1_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct solo1_state *s = (struct solo1_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->sbbase); + outb(0, s->sbbase+1); + outb(regb, s->sbbase+2); + outb(0, s->sbbase+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->sbbase+2; + } else { + regb = n.voice; + io = s->sbbase; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->sbbase + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->sbbase+2; + } else { + regb = n.voice; + io = s->sbbase; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->sbbase); + outb((p.kbd_split & 1) << 6, s->sbbase+1); + outb(0xbd, s->sbbase); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->sbbase+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->sbbase+2); + outb(arg, s->sbbase+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->sbbase+2); + outb(arg & 1, s->sbbase+3); + return 0; + + default: + return -EINVAL; + } +} + +static int solo1_dmfm_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct solo1_state *s = devs; + + while (s && s->dev_dmfm != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + /* init the stuff */ + outb(1, s->sbbase); + outb(0x20, s->sbbase+1); /* enable waveforms */ + outb(4, s->sbbase+2); + outb(0, s->sbbase+3); /* no 4op enabled */ + outb(5, s->sbbase+2); + outb(1, s->sbbase+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int solo1_dmfm_release(struct inode *inode, struct file *file) +{ + struct solo1_state *s = (struct solo1_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->sbbase); + outb(0, s->sbbase+1); + outb(regb, s->sbbase+2); + outb(0, s->sbbase+3); + } + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations solo1_dmfm_fops = { + &solo1_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &solo1_dmfm_ioctl, + NULL, /* mmap */ + &solo1_dmfm_open, + NULL, /* flush */ + &solo1_dmfm_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices */ +#define NR_DEVICE 5 + +/* --------------------------------------------------------------------- */ + +static struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 } +}; + +/*static*/ int __init init_solo1(void) +{ + struct solo1_state *s; + struct pci_dev *pcidev = NULL; + mm_segment_t fs; + int i, val, index = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "solo1: version v0.6 time " __TIME__ " " __DATE__ "\n"); + while (index < NR_DEVICE && + (pcidev = pci_find_device(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, pcidev))) { + if (pcidev->base_address[0] == 0 || + (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO || + pcidev->base_address[1] == 0 || + (pcidev->base_address[1] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO || + pcidev->base_address[2] == 0 || + (pcidev->base_address[2] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO || + pcidev->base_address[3] == 0 || + (pcidev->base_address[3] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->irq == 0) + continue; + if (!(s = kmalloc(sizeof(struct solo1_state), GFP_KERNEL))) { + printk(KERN_WARNING "solo1: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct solo1_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + s->magic = SOLO1_MAGIC; + s->pcidev = pcidev; + s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + s->sbbase = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + s->vcbase = pcidev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK; + s->ddmabase = s->vcbase + DDMABASE_OFFSET; + s->mpubase = pcidev->base_address[3] & PCI_BASE_ADDRESS_IO_MASK; + s->gpbase = pcidev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK; + s->irq = pcidev->irq; + if (check_region(s->iobase, IOBASE_EXTENT) || + check_region(s->sbbase, SBBASE_EXTENT) || + check_region(s->ddmabase, DDMABASE_EXTENT) || + check_region(s->mpubase, MPUBASE_EXTENT)) { + printk(KERN_ERR "solo1: io ports in use\n"); + goto err_region; + } + request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1"); + request_region(s->sbbase, SBBASE_EXTENT, "ESS Solo1"); + request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1"); + request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1"); + if (request_irq(s->irq, solo1_interrupt, SA_SHIRQ, "ESS Solo1", s)) { + printk(KERN_ERR "solo1: irq %u in use\n", s->irq); + goto err_irq; + } + /* initialize DDMA base address */ + printk(KERN_DEBUG "solo1: ddma base address: 0x%lx\n", s->ddmabase); + pci_write_config_word(pcidev, 0x60, (s->ddmabase & (~0xf)) | 1); + /* set DMA policy to DDMA, IRQ emulation off (CLKRUN disabled for now) */ + pci_write_config_dword(pcidev, 0x50, 0); + /* disable legacy audio address decode */ + pci_write_config_word(pcidev, 0x40, 0x907f); + + printk(KERN_INFO "solo1: joystick port at %#lx\n", s->gpbase+1); + + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) + goto err_dev2; + if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) + goto err_dev3; + if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) + goto err_dev4; + /* initialize the chips */ + outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ + + /* initialize mixer regs */ + write_mixer(s, 0x7f, 0); /* disable music digital recording */ + write_mixer(s, 0x7d, 0x0c); /* enable mic preamp, MONO_OUT is 2nd DAC right channel */ + write_mixer(s, 0x64, 0x45); /* volume control */ + write_mixer(s, 0x48, 0x10); /* enable music DAC/ES6xx interface */ + write_mixer(s, 0x50, 0); /* disable spatializer */ + write_mixer(s, 0x52, 0); + write_mixer(s, 0x14, 0); /* DAC1 minimum volume */ + write_mixer(s, 0x71, 0x20); /* enable new 0xA1 reg format */ + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(1, s->ddmabase+0xf); /* mask channel */ + /*outb(0, s->ddmabase+0x8);*/ /* enable controller (enable is low active!!) */ + + pci_set_master(pcidev); /* enable bus mastering */ + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + val = 1; /* enable mic preamp */ + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long)&val); + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + index++; + continue; + + err_dev4: + unregister_sound_dsp(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "solo1: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->iobase, IOBASE_EXTENT); + release_region(s->sbbase, SBBASE_EXTENT); + release_region(s->ddmabase, DDMABASE_EXTENT); + release_region(s->mpubase, MPUBASE_EXTENT); + err_region: + kfree_s(s, sizeof(struct solo1_state)); + } + if (!devs) + return -ENODEV; + return 0; +} + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ESS Solo1 Driver"); + +static void __exit cleanup_solo1(void) +{ + struct solo1_state *s; + + while ((s = devs)) { + devs = devs->next; + /* stop DMA controller */ + outb(0, s->iobase+6); + outb(0, s->ddmabase+0xd); /* DMA master clear */ + outb(3, s->sbbase+6); /* reset sequencer and FIFO */ + synchronize_irq(); + pci_write_config_word(s->pcidev, 0x60, 0); /* turn off DDMA controller address space */ + free_irq(s->irq, s); + release_region(s->iobase, IOBASE_EXTENT); + release_region(s->sbbase, SBBASE_EXTENT); + release_region(s->ddmabase, DDMABASE_EXTENT); + release_region(s->mpubase, MPUBASE_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree_s(s, sizeof(struct solo1_state)); + } + printk(KERN_INFO "solo1: unloading\n"); +} + +/* --------------------------------------------------------------------- */ + +module_init(init_solo1); +module_exit(cleanup_solo1); + + diff -u --recursive --new-file v2.2.11/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c --- v2.2.11/linux/drivers/sound/mad16.c Mon Aug 9 16:05:57 1999 +++ linux/drivers/sound/mad16.c Wed Aug 25 17:29:49 1999 @@ -889,7 +889,7 @@ int cdtype = 0; int cdirq = 0; int cdport = 0x340; -int cddma = 3; +int cddma = -1; int opl4 = 0; int joystick = 0; @@ -949,23 +949,28 @@ break; case 0x02: printk("Sony CDU31A"); - dmatype = 2; + dmatype = 1; + if(cddma == -1) cddma = 3; break; case 0x04: printk("Mitsumi"); - dmatype = 1; + dmatype = 0; + if(cddma == -1) cddma = 5; break; case 0x06: printk("Panasonic Lasermate"); - dmatype = 2; + dmatype = 1; + if(cddma == -1) cddma = 3; break; case 0x08: printk("Secondary IDE"); - dmatype = 1; + dmatype = 0; + if(cddma == -1) cddma = 5; break; case 0x0A: printk("Primary IDE"); - dmatype = 1; + dmatype = 0; + if(cddma == -1) cddma = 5; break; default: printk("\n"); @@ -973,8 +978,16 @@ return -EINVAL; } - if (dmatype) - { + /* + * Build the config words + */ + + mad16_conf = (joystick ^ 1) | cdtype; + mad16_cdsel = 0; + if (opl4) + mad16_cdsel |= 0x20; + + if(cdtype){ if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1) { printk("\n"); @@ -985,58 +998,51 @@ printk(", DMA %d", cddma); else printk(", no DMA"); - } - if (cdtype && !cdirq) - printk(", no IRQ"); - else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) - { - printk(", invalid IRQ (disabling)"); - cdirq = 0; - } - else printk(", IRQ %d", cdirq); - - printk(".\n"); - printk(KERN_INFO "Joystick port "); - if (joystick == 1) - printk("enabled.\n"); - else - { - joystick = 0; - printk("disabled.\n"); - } - /* - * Build the config words - */ + if (!cdirq) + printk(", no IRQ"); + else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) + { + printk(", invalid IRQ (disabling)"); + cdirq = 0; + } + else printk(", IRQ %d", cdirq); - mad16_conf = (joystick ^ 1) | cdtype; - mad16_cdsel = 0; - if (opl4) - mad16_cdsel |= 0x20; - mad16_cdsel |= dma_map[dmatype][cddma]; + mad16_cdsel |= dma_map[dmatype][cddma]; - if (cdtype < 0x08) - { - switch (cdport) + if (cdtype < 0x08) { - case 0x340: - mad16_cdsel |= 0x00; - break; - case 0x330: - mad16_cdsel |= 0x40; - break; - case 0x360: - mad16_cdsel |= 0x80; - break; - case 0x320: - mad16_cdsel |= 0xC0; - break; - default: - printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); - return -EINVAL; + switch (cdport) + { + case 0x340: + mad16_cdsel |= 0x00; + break; + case 0x330: + mad16_cdsel |= 0x40; + break; + case 0x360: + mad16_cdsel |= 0x80; + break; + case 0x320: + mad16_cdsel |= 0xC0; + break; + default: + printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); + return -EINVAL; + } } + mad16_cdsel |= irq_map[cdirq]; } - mad16_cdsel |= irq_map[cdirq]; + + printk(".\n"); + printk(KERN_INFO "Joystick port "); + if (joystick == 1) + printk("enabled.\n"); + else + { + joystick = 0; + printk("disabled.\n"); + } config.io_base = io; config.irq = irq; diff -u --recursive --new-file v2.2.11/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c --- v2.2.11/linux/drivers/sound/sb_common.c Mon Apr 12 16:18:27 1999 +++ linux/drivers/sound/sb_common.c Wed Aug 25 17:29:49 1999 @@ -897,7 +897,7 @@ } if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI)) { - if (devc->irq > 0); + if (devc->irq > 0) free_irq(devc->irq, devc); sound_unload_mixerdev(devc->my_mixerdev); diff -u --recursive --new-file v2.2.11/linux/drivers/sound/sb_ess.c linux/drivers/sound/sb_ess.c --- v2.2.11/linux/drivers/sound/sb_ess.c Mon Aug 9 16:05:57 1999 +++ linux/drivers/sound/sb_ess.c Wed Aug 25 17:29:49 1999 @@ -100,7 +100,7 @@ * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves * into ES1888 mode. This means that it claims IRQ 11, which happens to be my * ISDN adapter. Needless to say it no longer worked. I now understand why - * after rebooting 0x7f already was 0x05, the value of my choise: the BIOS + * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS * did it. * * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is decribed @@ -1200,10 +1200,10 @@ /* AAS: info stolen from ALSA: these boards have different clocks */ switch(devc->submodel) { -/* APPARENTLY NOT 1869 +/* APPARENTLY NOT 1869 AND 1887 case SUBMDL_ES1869: -*/ case SUBMDL_ES1887: +*/ case SUBMDL_ES1888: devc->caps |= SB_CAP_ES18XX_RATE; break; diff -u --recursive --new-file v2.2.11/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c --- v2.2.11/linux/drivers/sound/sound_core.c Mon Jun 7 11:06:06 1999 +++ linux/drivers/sound/sound_core.c Wed Aug 25 17:29:49 1999 @@ -67,6 +67,9 @@ #ifdef CONFIG_SOUND_MSNDPIN extern int msnd_pinnacle_init(void); #endif +#ifdef CONFIG_SOUND_ESSSOLO1 +extern int init_solo1(void); +#endif /* * Low level list operator. Scan the ordered list, find a hole and @@ -395,6 +398,9 @@ /* * Now init non OSS drivers */ +#ifdef CONFIG_SOUND_VWSND + init_vwsnd(); +#endif #ifdef CONFIG_SOUND_SONICVIBES init_sonicvibes(); #endif @@ -409,6 +415,9 @@ #endif #ifdef CONFIG_SOUND_MSNDPIN msnd_pinnacle_init(); +#endif +#ifdef CONFIG_SOUND_ESSSOLO1 + init_solo1(); #endif return 0; } diff -u --recursive --new-file v2.2.11/linux/drivers/sound/vwsnd.c linux/drivers/sound/vwsnd.c --- v2.2.11/linux/drivers/sound/vwsnd.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/vwsnd.c Wed Aug 25 17:29:49 1999 @@ -0,0 +1,3525 @@ +/* + * Sound driver for Silicon Graphics 320 and 540 Visual Workstations' + * onboard audio. See notes in ../../Documentation/sound/vwsnd . + * + * Copyright 1999 Silicon Graphics, Inc. All rights reserved. + * + * 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. + */ + +#undef VWSND_DEBUG /* define for debugging */ + +/* + * XXX to do - + * + * External sync. + * Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational. + * Bug - if select() called before read(), pcm_setup() not called. + * Bug - output doesn't stop soon enough if process killed. + */ + +/* + * Things to test - + * + * Will readv/writev work? Write a test. + * + * insmod/rmmod 100 million times. + * + * Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT + * rate). + * + * Concurrent threads banging on mixer simultaneously, both UP + * and SMP kernels. Especially, watch for thread A changing + * OUTSRC while thread B changes gain -- both write to the same + * ad1843 register. + * + * What happens if a client opens /dev/audio then forks? + * Do two procs have /dev/audio open? Test. + * + * Pump audio through the CD, MIC and line inputs and verify that + * they mix/mute into the output. + * + * Apps: + * amp + * mpg123 + * x11amp + * mxv + * kmedia + * esound + * need more input apps + * + * Run tests while bombarding with signals. setitimer(2) will do it... */ + +/* + * This driver is organized in nine sections. + * The nine sections are: + * + * debug stuff + * low level lithium access + * high level lithium access + * AD1843 access + * PCM I/O + * audio driver + * mixer driver + * probe/attach/unload + * initialization and loadable kernel module interface + * + * That is roughly the order of increasing abstraction, so forward + * dependencies are minimal. + */ + +/* + * Locking Notes + * + * INC_USE_COUNT and DEC_USE_COUNT keep track of the number of + * open descriptors to this driver. When the driver is compiled + * as a module, they call MOD_{INC,DEC}_USE_COUNT; otherwise they + * bump vwsnd_use_count. The global device list, vwsnd_dev_list, + * is immutable when the IN_USE is true. + * + * devc->open_lock is a semaphore that is used to enforce the + * single reader/single writer rule for /dev/audio. The rule is + * that each device may have at most one reader and one writer. + * Open will block until the previous client has closed the + * device, unless O_NONBLOCK is specified. + * + * The semaphore devc->io_sema serializes PCM I/O syscalls. This + * is unnecessary in Linux 2.2, because the kernel lock + * serializes read, write, and ioctl globally, but it's there, + * ready for the brave, new post-kernel-lock world. + * + * Locking between interrupt and baselevel is handled by the + * "lock" spinlock in vwsnd_port (one lock each for read and + * write). Each half holds the lock just long enough to see what + * area it owns and update its pointers. See pcm_output() and + * pcm_input() for most of the gory stuff. + * + * devc->mix_sema serializes all mixer ioctls. This is also + * redundant because of the kernel lock. + * + * The lowest level lock is lith->lithium_lock. It is a + * spinlock which is held during the two-register tango of + * reading/writing an AD1843 register. See + * li_{read,write}_ad1843_reg(). + */ + +/* + * Sample Format Notes + * + * Lithium's DMA engine has two formats: 16-bit 2's complement + * and 8-bit unsigned . 16-bit transfers the data unmodified, 2 + * bytes per sample. 8-bit unsigned transfers 1 byte per sample + * and XORs each byte with 0x80. Lithium can input or output + * either mono or stereo in either format. + * + * The AD1843 has four formats: 16-bit 2's complement, 8-bit + * unsigned, 8-bit mu-Law and 8-bit A-Law. + * + * This driver supports five formats: AFMT_S8, AFMT_U8, + * AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE. + * + * For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and + * rely on Lithium's XOR to translate between U8 and S8. + * + * For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR + * the 0x80 bit in software to compensate for Lithium's XOR. + * This happens in pcm_copy_{in,out}(). + */ + +#include +#include +#include +#include +#include +#include + +#include "sound_config.h" + +/*****************************************************************************/ +/* debug stuff */ + +#ifdef VWSND_DEBUG + +#include /* for in_interrupt() */ + +static int shut_up = 1; + +/* + * dbgassert - called when an assertion fails. + */ + +static void dbgassert(const char *fcn, int line, const char *expr) +{ + if (in_interrupt()) + panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n", + __FILE__, fcn, line, expr); + else { + int x; + printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n", + __FILE__, fcn, line, expr); + x = * (volatile int *) 0; /* force proc to exit */ + } +} + +/* + * Bunch of useful debug macros: + * + * ASSERT - print unless e nonzero (panic if in interrupt) + * DBGDO - include arbitrary code if debugging + * DBGX - debug print raw (w/o function name) + * DBGP - debug print w/ function name + * DBGE - debug print function entry + * DBGC - debug print function call + * DBGR - debug print function return + * DBGXV - debug print raw when verbose + * DBGPV - debug print when verbose + * DBGEV - debug print function entry when verbose + * DBGRV - debug print function return when verbose + */ + +#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e)) +#define DBGDO(x) x +#define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args)) +#define DBGP(fmt, args...) (DBGX(__FUNCTION__ ": " fmt, ##args)) +#define DBGE(fmt, args...) (DBGX(__FUNCTION__ fmt, ##args)) +#define DBGC(rtn) (DBGP("calling %s\n", rtn)) +#define DBGR() (DBGP("returning\n")) +#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args)) +#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args)) +#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args)) +#define DBGCV(rtn) (shut_up ? 0 : DBGC(rtn)) +#define DBGRV() (shut_up ? 0 : DBGR()) + +#else /* !VWSND_DEBUG */ + +#define ASSERT(e) ((void) 0) +#define DBGDO(x) /* don't */ +#define DBGX(fmt, args...) ((void) 0) +#define DBGP(fmt, args...) ((void) 0) +#define DBGE(fmt, args...) ((void) 0) +#define DBGC(rtn) ((void) 0) +#define DBGR() ((void) 0) +#define DBGPV(fmt, args...) ((void) 0) +#define DBGXV(fmt, args...) ((void) 0) +#define DBGEV(fmt, args...) ((void) 0) +#define DBGCV(rtn) ((void) 0) +#define DBGRV() ((void) 0) + +#endif /* !VWSND_DEBUG */ + +/*****************************************************************************/ +/* low level lithium access */ + +/* + * We need to talk to Lithium registers on three pages. Here are + * the pages' offsets from the base address (0xFF001000). + */ + +enum { + LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */ + LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */ + LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */ +}; + +/* low-level lithium data */ + +typedef struct lithium { + caddr_t page0; /* virtual addresses */ + caddr_t page1; + caddr_t page2; + spinlock_t lock; /* protects codec and UST/MSC access */ +} lithium_t; + +/* + * li_create initializes the lithium_t structure and sets up vm mappings + * to access the registers. + * Returns 0 on success, -errno on failure. + */ + +static int li_create(lithium_t *lith, unsigned long baseaddr) +{ + static void li_destroy(lithium_t *); + + lith->lock = SPIN_LOCK_UNLOCKED; + lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE); + lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE); + lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE); + if (!lith->page0 || !lith->page1 || !lith->page2) { + li_destroy(lith); + return -ENOMEM; + } + return 0; +} + +/* + * li_destroy destroys the lithium_t structure and vm mappings. + */ + +static void li_destroy(lithium_t *lith) +{ + if (lith->page0) { + iounmap(lith->page0); + lith->page0 = NULL; + } + if (lith->page1) { + iounmap(lith->page1); + lith->page1 = NULL; + } + if (lith->page2) { + iounmap(lith->page2); + lith->page2 = NULL; + } +} + +/* + * basic register accessors - read/write long/byte + */ + +static __inline__ unsigned long li_readl(lithium_t *lith, int off) +{ + return * (volatile unsigned long *) (lith->page0 + off); +} + +static __inline__ unsigned char li_readb(lithium_t *lith, int off) +{ + return * (volatile unsigned char *) (lith->page0 + off); +} + +static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val) +{ + * (volatile unsigned long *) (lith->page0 + off) = val; +} + +static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val) +{ + * (volatile unsigned char *) (lith->page0 + off) = val; +} + +/*****************************************************************************/ +/* High Level Lithium Access */ + +/* + * Lithium DMA Notes + * + * Lithium has two dedicated DMA channels for audio. They are known + * as comm1 and comm2 (communication areas 1 and 2). Comm1 is for + * input, and comm2 is for output. Each is controlled by three + * registers: BASE (base address), CFG (config) and CCTL + * (config/control). + * + * Each DMA channel points to a physically contiguous ring buffer in + * main memory of up to 8 Kbytes. (This driver always uses 8 Kb.) + * There are three pointers into the ring buffer: read, write, and + * trigger. The pointers are 8 bits each. Each pointer points to + * 32-byte "chunks" of data. The DMA engine moves 32 bytes at a time, + * so there is no finer-granularity control. + * + * In comm1, the hardware updates the write ptr, and software updates + * the read ptr. In comm2, it's the opposite: hardware updates the + * read ptr, and software updates the write ptr. I designate the + * hardware-updated ptr as the hwptr, and the software-updated ptr as + * the swptr. + * + * The trigger ptr and trigger mask are used to trigger interrupts. + * From the Lithium spec, section 5.6.8, revision of 12/15/1998: + * + * Trigger Mask Value + * + * A three bit wide field that represents a power of two mask + * that is used whenever the trigger pointer is compared to its + * respective read or write pointer. A value of zero here + * implies a mask of 0xFF and a value of seven implies a mask + * 0x01. This value can be used to sub-divide the ring buffer + * into pie sections so that interrupts monitor the progress of + * hardware from section to section. + * + * My interpretation of that is, whenever the hw ptr is updated, it is + * compared with the trigger ptr, and the result is masked by the + * trigger mask. (Actually, by the complement of the trigger mask.) + * If the result is zero, an interrupt is triggered. I.e., interrupt + * if ((hwptr & ~mask) == (trptr & ~mask)). The mask is formed from + * the trigger register value as mask = (1 << (8 - tmreg)) - 1. + * + * In yet different words, setting tmreg to 0 causes an interrupt after + * every 256 DMA chunks (8192 bytes) or once per traversal of the + * ring buffer. Setting it to 7 caues an interrupt every 2 DMA chunks + * (64 bytes) or 128 times per traversal of the ring buffer. + */ + +/* Lithium register offsets and bit definitions */ + +#define LI_HOST_CONTROLLER 0x000 +# define LI_HC_RESET 0x00008000 +# define LI_HC_LINK_ENABLE 0x00004000 +# define LI_HC_LINK_FAILURE 0x00000004 +# define LI_HC_LINK_CODEC 0x00000002 +# define LI_HC_LINK_READY 0x00000001 + +#define LI_INTR_STATUS 0x010 +#define LI_INTR_MASK 0x014 +# define LI_INTR_LINK_ERR 0x00008000 +# define LI_INTR_COMM2_TRIG 0x00000008 +# define LI_INTR_COMM2_UNDERFLOW 0x00000004 +# define LI_INTR_COMM1_TRIG 0x00000002 +# define LI_INTR_COMM1_OVERFLOW 0x00000001 + +#define LI_CODEC_COMMAND 0x018 +# define LI_CC_BUSY 0x00008000 +# define LI_CC_DIR 0x00000080 +# define LI_CC_DIR_RD LI_CC_DIR +# define LI_CC_DIR_WR (!LI_CC_DIR) +# define LI_CC_ADDR_MASK 0x0000007F + +#define LI_CODEC_DATA 0x01C + +#define LI_COMM1_BASE 0x100 +#define LI_COMM1_CTL 0x104 +# define LI_CCTL_RESET 0x80000000 +# define LI_CCTL_SIZE 0x70000000 +# define LI_CCTL_DMA_ENABLE 0x08000000 +# define LI_CCTL_TMASK 0x07000000 /* trigger mask */ +# define LI_CCTL_TPTR 0x00FF0000 /* trigger pointer */ +# define LI_CCTL_RPTR 0x0000FF00 +# define LI_CCTL_WPTR 0x000000FF +#define LI_COMM1_CFG 0x108 +# define LI_CCFG_LOCK 0x00008000 +# define LI_CCFG_SLOT 0x00000070 +# define LI_CCFG_DIRECTION 0x00000008 +# define LI_CCFG_DIR_IN (!LI_CCFG_DIRECTION) +# define LI_CCFG_DIR_OUT LI_CCFG_DIRECTION +# define LI_CCFG_MODE 0x00000004 +# define LI_CCFG_MODE_MONO (!LI_CCFG_MODE) +# define LI_CCFG_MODE_STEREO LI_CCFG_MODE +# define LI_CCFG_FORMAT 0x00000003 +# define LI_CCFG_FMT_8BIT 0x00000000 +# define LI_CCFG_FMT_16BIT 0x00000001 +#define LI_COMM2_BASE 0x10C +#define LI_COMM2_CTL 0x110 + /* bit definitions are the same as LI_COMM1_CTL */ +#define LI_COMM2_CFG 0x114 + /* bit definitions are the same as LI_COMM1_CFG */ + +#define LI_UST_LOW 0x200 /* 64-bit Unadjusted System Time is */ +#define LI_UST_HIGH 0x204 /* microseconds since boot */ + +#define LI_AUDIO1_UST 0x300 /* UST-MSC pairs */ +#define LI_AUDIO1_MSC 0x304 /* MSC (Media Stream Counter) */ +#define LI_AUDIO2_UST 0x308 /* counts samples actually */ +#define LI_AUDIO2_MSC 0x30C /* processed as of time UST */ + +/* + * Lithium's DMA engine operates on chunks of 32 bytes. We call that + * a DMACHUNK. + */ + +#define DMACHUNK_SHIFT 5 +#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT) +#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT) +#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT) + +/* + * Two convenient macros to shift bitfields into/out of position. + * + * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)). + * As long as mask is constant, we trust the compiler will change the + * multipy and divide into shifts. + */ + +#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask)) +#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask))) + +/* + * dma_chan_desc is invariant information about a Lithium + * DMA channel. There are two instances, li_comm1 and li_comm2. + * + * Note that the CCTL register fields are write ptr and read ptr, but what + * we care about are which pointer is updated by software and which by + * hardware. + */ + +typedef struct dma_chan_desc { + int basereg; + int cfgreg; + int ctlreg; + int hwptrreg; + int swptrreg; + int ustreg; + int mscreg; + unsigned long swptrmask; + int ad1843_slot; + int direction; /* LI_CCTL_DIR_IN/OUT */ +} dma_chan_desc_t; + +static const dma_chan_desc_t li_comm1 = { + LI_COMM1_BASE, /* base register offset */ + LI_COMM1_CFG, /* config register offset */ + LI_COMM1_CTL, /* control register offset */ + LI_COMM1_CTL + 0, /* hw ptr reg offset (write ptr) */ + LI_COMM1_CTL + 1, /* sw ptr reg offset (read ptr) */ + LI_AUDIO1_UST, /* ust reg offset */ + LI_AUDIO1_MSC, /* msc reg offset */ + LI_CCTL_RPTR, /* sw ptr bitmask in ctlval */ + 2, /* ad1843 serial slot */ + LI_CCFG_DIR_IN /* direction */ +}; + +static const dma_chan_desc_t li_comm2 = { + LI_COMM2_BASE, /* base register offset */ + LI_COMM2_CFG, /* config register offset */ + LI_COMM2_CTL, /* control register offset */ + LI_COMM2_CTL + 1, /* hw ptr reg offset (read ptr) */ + LI_COMM2_CTL + 0, /* sw ptr reg offset (writr ptr) */ + LI_AUDIO2_UST, /* ust reg offset */ + LI_AUDIO2_MSC, /* msc reg offset */ + LI_CCTL_WPTR, /* sw ptr bitmask in ctlval */ + 2, /* ad1843 serial slot */ + LI_CCFG_DIR_OUT /* direction */ +}; + +/* + * dma_chan is variable information about a Lithium DMA channel. + * + * The desc field points to invariant information. + * The lith field points to a lithium_t which is passed + * to li_read* and li_write* to access the registers. + * The *val fields shadow the lithium registers' contents. + */ + +typedef struct dma_chan { + const dma_chan_desc_t *desc; + lithium_t *lith; + unsigned long baseval; + unsigned long cfgval; + unsigned long ctlval; +} dma_chan_t; + +/* + * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter). + * UST is time in microseconds since the system booted, and MSC is a + * counter that increments with every audio sample. + */ + +typedef struct ustmsc { + unsigned long long ust; + unsigned long msc; +} ustmsc_t; + +/* + * li_ad1843_wait waits until lithium says the AD1843 register + * exchange is not busy. Returns 0 on success, -EBUSY on timeout. + * + * Locking: must be called with lithium_lock held. + */ + +static int li_ad1843_wait(lithium_t *lith) +{ + unsigned long later = jiffies + 2; + while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY) + if (jiffies >= later) + return -EBUSY; + return 0; +} + +/* + * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register. + * + * Returns unsigned register value on success, -errno on failure. + */ + +static int li_read_ad1843_reg(lithium_t *lith, int reg) +{ + int val; + + ASSERT(!in_interrupt()); + spin_lock(&lith->lock); + { + val = li_ad1843_wait(lith); + if (val == 0) { + li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg); + val = li_ad1843_wait(lith); + } + if (val == 0) + val = li_readl(lith, LI_CODEC_DATA); + } + spin_unlock(&lith->lock); + + DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n", + lith, reg, val); + + return val; +} + +/* + * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register. + */ + +static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval) +{ + spin_lock(&lith->lock); + { + if (li_ad1843_wait(lith) == 0) { + li_writel(lith, LI_CODEC_DATA, newval); + li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg); + } + } + spin_unlock(&lith->lock); +} + +/* + * li_setup_dma calculates all the register settings for DMA in a particular + * mode. It takes too many arguments. + */ + +static void li_setup_dma(dma_chan_t *chan, + const dma_chan_desc_t *desc, + lithium_t *lith, + unsigned long buffer_paddr, + int bufshift, + int fragshift, + int channels, + int sampsize) +{ + unsigned long mode, format; + unsigned long size, tmask; + + DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, " + "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n", + chan, desc, lith, buffer_paddr, + bufshift, fragshift, channels, sampsize); + + /* Reset the channel first. */ + + li_writel(lith, desc->ctlreg, LI_CCTL_RESET); + + ASSERT(channels == 1 || channels == 2); + if (channels == 2) + mode = LI_CCFG_MODE_STEREO; + else + mode = LI_CCFG_MODE_MONO; + ASSERT(sampsize == 1 || sampsize == 2); + if (sampsize == 2) + format = LI_CCFG_FMT_16BIT; + else + format = LI_CCFG_FMT_8BIT; + chan->desc = desc; + chan->lith = lith; + + /* + * Lithium DMA address register takes a 40-bit physical + * address, right-shifted by 8 so it fits in 32 bits. Bit 37 + * must be set -- it enables cache coherence. + */ + + ASSERT(!(buffer_paddr & 0xFF)); + chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8); + + chan->cfgval = (!LI_CCFG_LOCK | + SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) | + desc->direction | + mode | + format); + + size = bufshift - 6; + tmask = 13 - fragshift; /* See Lithium DMA Notes above. */ + ASSERT(size >= 2 && size <= 7); + ASSERT(tmask >= 1 && tmask <= 7); + chan->ctlval = (!LI_CCTL_RESET | + SHIFT_FIELD(size, LI_CCTL_SIZE) | + !LI_CCTL_DMA_ENABLE | + SHIFT_FIELD(tmask, LI_CCTL_TMASK) | + SHIFT_FIELD(0, LI_CCTL_TPTR)); + + DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval); + DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval); + DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval); + + li_writel(lith, desc->basereg, chan->baseval); + li_writel(lith, desc->cfgreg, chan->cfgval); + li_writel(lith, desc->ctlreg, chan->ctlval); + + DBGRV(); +} + +static void li_shutdown_dma(dma_chan_t *chan) +{ + lithium_t *lith = chan->lith; + caddr_t lith1 = lith->page1; + + DBGEV("(chan=0x%p)\n", chan); + + chan->ctlval &= ~LI_CCTL_DMA_ENABLE; + DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); + li_writel(lith, chan->desc->ctlreg, chan->ctlval); + + /* + * Offset 0x500 on Lithium page 1 is an undocumented, + * unsupported register that holds the zero sample value. + * Lithium is supposed to output zero samples when DMA is + * inactive, and repeat the last sample when DMA underflows. + * But it has a bug, where, after underflow occurs, the zero + * sample is not reset. + * + * I expect this to break in a future rev of Lithium. + */ + + if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT) + * (volatile unsigned long *) (lith1 + 0x500) = 0; +} + +/* + * li_activate_dma always starts dma at the beginning of the buffer. + * + * N.B., these may be called from interrupt. + */ + +static __inline__ void li_activate_dma(dma_chan_t *chan) +{ + chan->ctlval |= LI_CCTL_DMA_ENABLE; + DBGPV("ctlval = 0x%lx\n", chan->ctlval); + li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval); +} + +static void li_deactivate_dma(dma_chan_t *chan) +{ + lithium_t *lith = chan->lith; + caddr_t lith2 = lith->page2; + + chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR); + DBGPV("ctlval = 0x%lx\n", chan->ctlval); + DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval); + li_writel(lith, chan->desc->ctlreg, chan->ctlval); + + /* + * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented, + * unsupported registers that are internal copies of the DMA + * read and write pointers. Because of a Lithium bug, these + * registers aren't zeroed correctly when DMA is shut off. So + * we whack them directly. + * + * I expect this to break in a future rev of Lithium. + */ + + if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) { + * (volatile unsigned long *) (lith2 + 0x98) = 0; + * (volatile unsigned long *) (lith2 + 0x9C) = 0; + } +} + +/* + * read/write the ring buffer pointers. These routines' arguments and results + * are byte offsets from the beginning of the ring buffer. + */ + +static __inline__ int li_read_swptr(dma_chan_t *chan) +{ + const unsigned long mask = chan->desc->swptrmask; + + return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask)); +} + +static __inline__ int li_read_hwptr(dma_chan_t *chan) +{ + return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg)); +} + +static __inline__ void li_write_swptr(dma_chan_t *chan, int val) +{ + const unsigned long mask = chan->desc->swptrmask; + + ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF))); + val = BYTES_TO_CHUNKS(val); + chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask); + li_writeb(chan->lith, chan->desc->swptrreg, val); +} + +/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */ + +static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc) +{ + lithium_t *lith = chan->lith; + const dma_chan_desc_t *desc = chan->desc; + unsigned long now_low, now_high0, now_high1, chan_ust; + + spin_lock(&lith->lock); + { + /* + * retry until we do all five reads without the + * high word changing. (High word increments + * every 2^32 microseconds, i.e., not often) + */ + do { + now_high0 = li_readl(lith, LI_UST_HIGH); + now_low = li_readl(lith, LI_UST_LOW); + + /* + * Lithium guarantees these two reads will be + * atomic -- ust will not increment after msc + * is read. + */ + + ustmsc->msc = li_readl(lith, desc->mscreg); + chan_ust = li_readl(lith, desc->ustreg); + + now_high1 = li_readl(lith, LI_UST_HIGH); + } while (now_high0 != now_high1); + } + spin_unlock(&lith->lock); + ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust); +} + +static void li_enable_interrupts(lithium_t *lith, unsigned int mask) +{ + DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); + + /* clear any already-pending interrupts. */ + + li_writel(lith, LI_INTR_STATUS, mask); + + /* enable the interrupts. */ + + mask |= li_readl(lith, LI_INTR_MASK); + li_writel(lith, LI_INTR_MASK, mask); +} + +static void li_disable_interrupts(lithium_t *lith, unsigned int mask) +{ + unsigned int keepmask; + + DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask); + + /* disable the interrupts */ + + keepmask = li_readl(lith, LI_INTR_MASK) & ~mask; + li_writel(lith, LI_INTR_MASK, keepmask); + + /* clear any pending interrupts. */ + + li_writel(lith, LI_INTR_STATUS, mask); +} + +/* Get the interrupt status and clear all pending interrupts. */ + +static unsigned int li_get_clear_intr_status(lithium_t *lith) +{ + unsigned int status; + + status = li_readl(lith, LI_INTR_STATUS); + li_writel(lith, LI_INTR_STATUS, ~0); + return status & li_readl(lith, LI_INTR_MASK); +} + +static int li_init(lithium_t *lith) +{ + /* 1. System power supplies stabilize. */ + + /* 2. Assert the ~RESET signal. */ + + li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET); + udelay(1); + + /* 3. Deassert the ~RESET signal and enter a wait period to allow + the AD1843 internal clocks and the external crystal oscillator + to stabilize. */ + + li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); + udelay(1); + + return 0; +} + +/*****************************************************************************/ +/* AD1843 access */ + +/* + * AD1843 bitfield definitions. All are named as in the AD1843 data + * sheet, with ad1843_ prepended and individual bit numbers removed. + * + * E.g., bits LSS0 through LSS2 become ad1843_LSS. + * + * Only the bitfields we need are defined. + */ + +typedef struct ad1843_bitfield { + char reg; + char lo_bit; + char nbits; +} ad1843_bitfield_t; + +static const ad1843_bitfield_t + ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */ + ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */ + ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */ + ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */ + ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */ + ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */ + ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */ + ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */ + ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */ + ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */ + ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */ + ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */ + ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */ + ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */ + ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */ + ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */ + ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */ + ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */ + ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */ + ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */ + ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */ + ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */ + ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */ + ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */ + ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */ + ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */ + ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */ + ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */ + ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */ + ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */ + ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */ + ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */ + ad1843_C2C = { 20, 0, 16 }, /* Clock 1 Sample Rate Select */ + ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */ + ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */ + ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */ + ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */ + ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */ + ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */ + ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */ + ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */ + ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */ + ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */ + ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */ + ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */ + ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */ + ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */ + ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */ + ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */ + ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */ + ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down */ + +/* + * The various registers of the AD1843 use three different formats for + * specifying gain. The ad1843_gain structure parameterizes the + * formats. + */ + +typedef struct ad1843_gain { + + int negative; /* nonzero if gain is negative. */ + const ad1843_bitfield_t *lfield; + const ad1843_bitfield_t *rfield; + +} ad1843_gain_t; + +static const ad1843_gain_t ad1843_gain_RECLEV + = { 0, &ad1843_LIG, &ad1843_RIG }; +static const ad1843_gain_t ad1843_gain_LINE + = { 1, &ad1843_LX1M, &ad1843_RX1M }; +static const ad1843_gain_t ad1843_gain_CD + = { 1, &ad1843_LX2M, &ad1843_RX2M }; +static const ad1843_gain_t ad1843_gain_MIC + = { 1, &ad1843_LMCM, &ad1843_RMCM }; +static const ad1843_gain_t ad1843_gain_PCM + = { 1, &ad1843_LDA1G, &ad1843_RDA1G }; + +/* read the current value of an AD1843 bitfield. */ + +static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field) +{ + int w = li_read_ad1843_reg(lith, field->reg); + int val = w >> field->lo_bit & ((1 << field->nbits) - 1); + + DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n", + lith, field->reg, field->lo_bit, field->nbits, val); + + return val; +} + +/* + * write a new value to an AD1843 bitfield and return the old value. + */ + +static int ad1843_write_bits(lithium_t *lith, + const ad1843_bitfield_t *field, + int newval) +{ + int w = li_read_ad1843_reg(lith, field->reg); + int mask = ((1 << field->nbits) - 1) << field->lo_bit; + int oldval = (w & mask) >> field->lo_bit; + int newbits = (newval << field->lo_bit) & mask; + w = (w & ~mask) | newbits; + (void) li_write_ad1843_reg(lith, field->reg, w); + + DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) " + "returns 0x%x\n", + lith, field->reg, field->lo_bit, field->nbits, newval, + oldval); + + return oldval; +} + +/* + * ad1843_read_multi reads multiple bitfields from the same AD1843 + * register. It uses a single read cycle to do it. (Reading the + * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20 + * microseconds.) + * + * Called ike this. + * + * ad1843_read_multi(lith, nfields, + * &ad1843_FIELD1, &val1, + * &ad1843_FIELD2, &val2, ...); + */ + +static void ad1843_read_multi(lithium_t *lith, int argcount, ...) +{ + va_list ap; + const ad1843_bitfield_t *fp; + int w = 0, mask, *value, reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const ad1843_bitfield_t *); + value = va_arg(ap, int *); + if (reg == -1) { + reg = fp->reg; + w = li_read_ad1843_reg(lith, reg); + } + ASSERT(reg == fp->reg); + mask = (1 << fp->nbits) - 1; + *value = w >> fp->lo_bit & mask; + } + va_end(ap); +} + +/* + * ad1843_write_multi stores multiple bitfields into the same AD1843 + * register. It uses one read and one write cycle to do it. + * + * Called like this. + * + * ad1843_write_multi(lith, nfields, + * &ad1843_FIELD1, val1, + * &ad1843_FIELF2, val2, ...); + */ + +static void ad1843_write_multi(lithium_t *lith, int argcount, ...) +{ + va_list ap; + int reg; + const ad1843_bitfield_t *fp; + int value; + int w, m, mask, bits; + + mask = 0; + bits = 0; + reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const ad1843_bitfield_t *); + value = va_arg(ap, int); + if (reg == -1) + reg = fp->reg; + ASSERT(fp->reg == reg); + m = ((1 << fp->nbits) - 1) << fp->lo_bit; + mask |= m; + bits |= (value << fp->lo_bit) & m; + } + va_end(ap); + ASSERT(!(bits & ~mask)); + if (~mask & 0xFFFF) + w = li_read_ad1843_reg(lith, reg); + else + w = 0; + w = (w & ~mask) | bits; + (void) li_write_ad1843_reg(lith, reg, w); +} + +/* + * ad1843_get_gain reads the specified register and extracts the gain value + * using the supplied gain type. It returns the gain in OSS format. + */ + +static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp) +{ + int lg, rg; + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg); + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + lg = (lg * 100 + (mask >> 1)) / mask; + rg = (rg * 100 + (mask >> 1)) / mask; + return lg << 0 | rg << 8; +} + +/* + * Set an audio channel's gain. Converts from OSS format to AD1843's + * format. + * + * Returns the new gain, which may be lower than the old gain. + */ + +static int ad1843_set_gain(lithium_t *lith, + const ad1843_gain_t *gp, + int newval) +{ + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + int lg = newval >> 0 & 0xFF; + int rg = newval >> 8; + if (lg < 0 || lg > 100 || rg < 0 || rg > 100) + return -EINVAL; + lg = (lg * mask + (mask >> 1)) / 100; + rg = (rg * mask + (mask >> 1)) / 100; + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg); + return ad1843_get_gain(lith, gp); +} + +/* Returns the current recording source, in OSS format. */ + +static int ad1843_get_recsrc(lithium_t *lith) +{ + int ls = ad1843_read_bits(lith, &ad1843_LSS); + + switch (ls) { + case 1: + return SOUND_MASK_MIC; + case 2: + return SOUND_MASK_LINE; + case 3: + return SOUND_MASK_CD; + case 6: + return SOUND_MASK_PCM; + default: + ASSERT(0); + return -1; + } +} + +/* + * Enable/disable digital resample mode in the AD1843. + * + * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down + * while switching modes. So we save DA1's state (DA2's state is not + * interesting), power them down, switch into/out of resample mode, + * power them up, and restore state. + * + * This will cause audible glitches if D/A or A/D is going on, so the + * driver disallows that (in mixer_write_ioctl()). + * + * The open question is, is this worth doing? I'm leaving it in, + * because it's written, but... + */ + +static void ad1843_set_resample_mode(lithium_t *lith, int onoff) +{ + /* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */ + int save_da1 = li_read_ad1843_reg(lith, 9); + + /* Power down A/D and D/A. */ + ad1843_write_multi(lith, 4, + &ad1843_DA1EN, 0, + &ad1843_DA2EN, 0, + &ad1843_ADLEN, 0, + &ad1843_ADREN, 0); + + /* Switch mode */ + ASSERT(onoff == 0 || onoff == 1); + ad1843_write_bits(lith, &ad1843_DRSFLT, onoff); + + /* Power up A/D and D/A. */ + ad1843_write_multi(lith, 3, + &ad1843_DA1EN, 1, + &ad1843_ADLEN, 1, + &ad1843_ADREN, 1); + + /* Restore DA1 mute and gain. */ + li_write_ad1843_reg(lith, 9, save_da1); +} + +/* + * Set recording source. Arg newsrc specifies an OSS channel mask. + * + * The complication is that when we switch into/out of loopback mode + * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of + * digital resampling mode. + * + * Returns newsrc on success, -errno on failure. + */ + +static int ad1843_set_recsrc(lithium_t *lith, int newsrc) +{ + int bits; + int oldbits; + + switch (newsrc) { + case SOUND_MASK_PCM: + bits = 6; + break; + + case SOUND_MASK_MIC: + bits = 1; + break; + + case SOUND_MASK_LINE: + bits = 2; + break; + + case SOUND_MASK_CD: + bits = 3; + break; + + default: + return -EINVAL; + } + oldbits = ad1843_read_bits(lith, &ad1843_LSS); + if (newsrc == SOUND_MASK_PCM && oldbits != 6) { + DBGP("enabling digital resample mode\n"); + ad1843_set_resample_mode(lith, 1); + ad1843_write_multi(lith, 2, + &ad1843_DAADL, 2, + &ad1843_DAADR, 2); + } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) { + DBGP("disabling digital resample mode\n"); + ad1843_set_resample_mode(lith, 0); + ad1843_write_multi(lith, 2, + &ad1843_DAADL, 0, + &ad1843_DAADR, 0); + } + ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits); + return newsrc; +} + +/* + * Return current output sources, in OSS format. + */ + +static int ad1843_get_outsrc(lithium_t *lith) +{ + int pcm, line, mic, cd; + + pcm = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM; + line = ad1843_read_bits(lith, &ad1843_LX1MM) ? 0 : SOUND_MASK_LINE; + cd = ad1843_read_bits(lith, &ad1843_LX2MM) ? 0 : SOUND_MASK_CD; + mic = ad1843_read_bits(lith, &ad1843_LMCMM) ? 0 : SOUND_MASK_MIC; + + return pcm | line | cd | mic; +} + +/* + * Set output sources. Arg is a mask of active sources in OSS format. + * + * Returns source mask on success, -errno on failure. + */ + +static int ad1843_set_outsrc(lithium_t *lith, int mask) +{ + int pcm, line, mic, cd; + + if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_CD | SOUND_MASK_MIC)) + return -EINVAL; + pcm = (mask & SOUND_MASK_PCM) ? 0 : 1; + line = (mask & SOUND_MASK_LINE) ? 0 : 1; + mic = (mask & SOUND_MASK_MIC) ? 0 : 1; + cd = (mask & SOUND_MASK_CD) ? 0 : 1; + + ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm); + ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line); + ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd, &ad1843_RX2MM, cd); + ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic, &ad1843_RMCMM, mic); + + return mask; +} + +/* Setup ad1843 for D/A conversion. */ + +static void ad1843_setup_dac(lithium_t *lith, + int framerate, + int fmt, + int channels) +{ + int ad_fmt = 0, ad_mode = 0; + + DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", + lith, framerate, fmt, channels); + + switch (fmt) { + case AFMT_S8: ad_fmt = 1; break; + case AFMT_U8: ad_fmt = 1; break; + case AFMT_S16_LE: ad_fmt = 1; break; + case AFMT_MU_LAW: ad_fmt = 2; break; + case AFMT_A_LAW: ad_fmt = 3; break; + default: ASSERT(0); + } + + switch (channels) { + case 2: ad_mode = 0; break; + case 1: ad_mode = 1; break; + default: ASSERT(0); + } + + DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt); + ASSERT(framerate >= 4000 && framerate <= 49000); + ad1843_write_bits(lith, &ad1843_C1C, framerate); + ad1843_write_multi(lith, 2, + &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt); +} + +static void ad1843_shutdown_dac(lithium_t *lith) +{ + ad1843_write_bits(lith, &ad1843_DA1F, 1); +} + +static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels) +{ + int da_fmt = 0; + + DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n", + lith, framerate, fmt, channels); + + switch (fmt) { + case AFMT_S8: da_fmt = 1; break; + case AFMT_U8: da_fmt = 1; break; + case AFMT_S16_LE: da_fmt = 1; break; + case AFMT_MU_LAW: da_fmt = 2; break; + case AFMT_A_LAW: da_fmt = 3; break; + default: ASSERT(0); + } + + DBGPV("da_fmt = %d\n", da_fmt); + ASSERT(framerate >= 4000 && framerate <= 49000); + ad1843_write_bits(lith, &ad1843_C2C, framerate); + ad1843_write_multi(lith, 2, + &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt); +} + +static void ad1843_shutdown_adc(lithium_t *lith) +{ + /* nothing to do */ +} + +/* + * Fully initialize the ad1843. As described in the AD1843 data + * sheet, section "START-UP SEQUENCE". The numbered comments are + * subsection headings from the data sheet. See the data sheet, pages + * 52-54, for more info. + * + * return 0 on success, -errno on failure. */ + +static int ad1843_init(lithium_t *lith) +{ + unsigned long later; + int err; + + err = li_init(lith); + if (err) + return err; + + if (ad1843_read_bits(lith, &ad1843_INIT) != 0) { + printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n"); + return -EIO; + } + + ad1843_write_bits(lith, &ad1843_SCF, 1); + + /* 4. Put the conversion resources into standby. */ + + ad1843_write_bits(lith, &ad1843_PDNI, 0); + later = jiffies + HZ / 2; /* roughly half a second */ + DBGDO(shut_up++); + while (ad1843_read_bits(lith, &ad1843_PDNO)) { + if (jiffies > later) { + printk(KERN_ERR + "vwsnd audio: AD1843 won't power up\n"); + return -EIO; + } + schedule(); + } + DBGDO(shut_up--); + + /* 5. Power up the clock generators and enable clock output pins. */ + + ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1); + + /* 6. Configure conversion resources while they are in standby. */ + + /* DAC1 uses clock 1 as source, ADC uses clock 2. Always. */ + + ad1843_write_multi(lith, 3, + &ad1843_DA1C, 1, + &ad1843_ADLC, 2, + &ad1843_ADRC, 2); + + /* 7. Enable conversion resources. */ + + ad1843_write_bits(lith, &ad1843_ADTLK, 1); + ad1843_write_multi(lith, 5, + &ad1843_ANAEN, 1, + &ad1843_AAMEN, 1, + &ad1843_DA1EN, 1, + &ad1843_ADLEN, 1, + &ad1843_ADREN, 1); + + /* 8. Configure conversion resources while they are enabled. */ + + ad1843_write_bits(lith, &ad1843_DA1C, 1); + + /* Unmute all channels. */ + + ad1843_set_outsrc(lith, + (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD)); + ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0); + + /* Set default recording source to Line In and set + * mic gain to +20 dB. + */ + + ad1843_set_recsrc(lith, SOUND_MASK_LINE); + ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1); + + /* Set Speaker Out level to +/- 4V and unmute it. */ + + ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0); + + return 0; +} + +/*****************************************************************************/ +/* PCM I/O */ + +#define READ_INTR_MASK (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW) +#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW) + +typedef enum vwsnd_port_swstate { /* software state */ + SW_OFF, + SW_INITIAL, + SW_RUN, + SW_DRAIN, +} vwsnd_port_swstate_t; + +typedef enum vwsnd_port_hwstate { /* hardware state */ + HW_STOPPED, + HW_RUNNING, +} vwsnd_port_hwstate_t; + +/* + * These flags are read by ISR, but only written at baseline. + */ + +typedef enum vwsnd_port_flags { + DISABLED = 1 << 0, + ERFLOWN = 1 << 1, /* overflown or underflown */ + HW_BUSY = 1 << 2, +} vwsnd_port_flags_t; + +/* + * vwsnd_port is the per-port data structure. Each device has two + * ports, one for input and one for output. + * + * Locking: + * + * port->lock protects: hwstate, flags, swb_[iu]_avail. + * + * devc->io_sema protects: swstate, sw_*, swb_[iu]_idx. + * + * everything else is only written by open/release or + * pcm_{setup,shutdown}(), which are serialized by a + * combination of devc->open_sema and devc->io_sema. + */ + +typedef struct vwsnd_port { + + spinlock_t lock; + struct wait_queue *queue; + vwsnd_port_swstate_t swstate; + vwsnd_port_hwstate_t hwstate; + vwsnd_port_flags_t flags; + + int sw_channels; + int sw_samplefmt; + int sw_framerate; + int sample_size; + int frame_size; + unsigned int zero_word; /* zero for the sample format */ + + int sw_fragshift; + int sw_fragcount; + int sw_subdivshift; + + unsigned int hw_fragshift; + unsigned int hw_fragsize; + unsigned int hw_fragcount; + + int hwbuf_size; + unsigned long hwbuf_paddr; + unsigned long hwbuf_vaddr; + caddr_t hwbuf; /* hwbuf == hwbuf_vaddr */ + int hwbuf_max; /* max bytes to preload */ + + caddr_t swbuf; + unsigned int swbuf_size; /* size in bytes */ + unsigned int swb_u_idx; /* index of next user byte */ + unsigned int swb_i_idx; /* index of next intr byte */ + unsigned int swb_u_avail; /* # bytes avail to user */ + unsigned int swb_i_avail; /* # bytes avail to intr */ + + dma_chan_t chan; + + /* Accounting */ + + int byte_count; + int frag_count; + int MSC_offset; + +} vwsnd_port_t; + +/* vwsnd_dev is the per-device data structure. */ + +typedef struct vwsnd_dev { + struct vwsnd_dev *next_dev; + int audio_minor; /* minor number of audio device */ + int mixer_minor; /* minor number of mixer device */ + + struct semaphore open_sema; + struct semaphore io_sema; + struct semaphore mix_sema; + mode_t open_mode; + struct wait_queue *open_wait; + + lithium_t lith; + + vwsnd_port_t rport; + vwsnd_port_t wport; +} vwsnd_dev_t; + +static vwsnd_dev_t *vwsnd_dev_list; /* linked list of all devices */ + +#ifdef MODULE + +# define INC_USE_COUNT MOD_INC_USE_COUNT +# define DEC_USE_COUNT MOD_DEC_USE_COUNT +# define IN_USE MOD_IN_USE + +#else + +static atomic_t vwsnd_use_count = ATOMIC_INIT(0); + +# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count)) +# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count)) +# define IN_USE (atomic_read(&vwsnd_use_count) != 0) + +#endif + +/* + * Lithium can only DMA multiples of 32 bytes. Its DMA buffer may + * be up to 8 Kb. This driver always uses 8 Kb. + * + * Memory bug workaround -- I'm not sure what's going on here, but + * somehow pcm_copy_out() was triggering segv's going on to the next + * page of the hw buffer. So, I make the hw buffer one size bigger + * than we actually use. That way, the following page is allocated + * and mapped, and no error. I suspect that something is broken + * in Cobalt, but haven't really investigated. HBO is the actual + * size of the buffer, and HWBUF_ORDER is what we allocate. + */ + +#define HWBUF_SHIFT 13 +#define HWBUF_SIZE (1 << HWBUF_SHIFT) +# define HBO (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0) +# define HWBUF_ORDER (HBO + 1) /* next size bigger */ +#define MIN_SPEED 4000 +#define MAX_SPEED 49000 + +#define MIN_FRAGSHIFT (DMACHUNK_SHIFT + 1) +#define MAX_FRAGSHIFT (PAGE_SHIFT) +#define MIN_FRAGSIZE (1 << MIN_FRAGSHIFT) +#define MAX_FRAGSIZE (1 << MAX_FRAGSHIFT) +#define MIN_FRAGCOUNT(fragsize) 3 +#define MAX_FRAGCOUNT(fragsize) (32 * PAGE_SIZE / (fragsize)) +#define DEFAULT_FRAGSHIFT 12 +#define DEFAULT_FRAGCOUNT 16 +#define DEFAULT_SUBDIVSHIFT 0 + +/* + * The software buffer (swbuf) is a ring buffer shared between user + * level and interrupt level. Each level owns some of the bytes in + * the buffer, and may give bytes away by calling swb_inc_{u,i}(). + * User level calls _u for user, and interrupt level calls _i for + * interrupt. + * + * port->swb_{u,i}_avail is the number of bytes available to that level. + * + * port->swb_{u,i}_idx is the index of the first available byte in the + * buffer. + * + * Each level calls swb_inc_{u,i}() to atomically increment its index, + * recalculate the number of bytes available for both sides, and + * return the number of bytes available. Since each side can only + * give away bytes, the other side can only increase the number of + * bytes available to this side. Each side updates its own index + * variable, swb_{u,i}_idx, so no lock is needed to read it. + * + * To query the number of bytes available, call swb_inc_{u,i} with an + * increment of zero. + */ + +static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc) +{ + if (inc) { + port->swb_u_idx += inc; + port->swb_u_idx %= port->swbuf_size; + port->swb_u_avail -= inc; + port->swb_i_avail += inc; + } + return port->swb_u_avail; +} + +static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + { + ret = __swb_inc_u(port, inc); + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc) +{ + if (inc) { + port->swb_i_idx += inc; + port->swb_i_idx %= port->swbuf_size; + port->swb_i_avail -= inc; + port->swb_u_avail += inc; + } + return port->swb_i_avail; +} + +static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + { + ret = __swb_inc_i(port, inc); + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +/* + * pcm_setup - this routine initializes all port state after + * mode-setting ioctls have been done, but before the first I/O is + * done. + * + * Locking: called with devc->io_sema held. + * + * Returns 0 on success, -errno on failure. + */ + +static int pcm_setup(vwsnd_dev_t *devc, + vwsnd_port_t *rport, + vwsnd_port_t *wport) +{ + vwsnd_port_t *aport = rport ? rport : wport; + int sample_size; + unsigned int zero_word; + + DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); + + ASSERT(aport != NULL); + if (aport->swbuf != NULL) + return 0; + switch (aport->sw_samplefmt) { + case AFMT_MU_LAW: + sample_size = 1; + zero_word = 0xFFFFFFFF ^ 0x80808080; + break; + + case AFMT_A_LAW: + sample_size = 1; + zero_word = 0xD5D5D5D5 ^ 0x80808080; + break; + + case AFMT_U8: + sample_size = 1; + zero_word = 0x80808080; + break; + + case AFMT_S8: + sample_size = 1; + zero_word = 0x00000000; + break; + + case AFMT_S16_LE: + sample_size = 2; + zero_word = 0x00000000; + break; + + default: + sample_size = 0; /* prevent compiler warning */ + zero_word = 0; + ASSERT(0); + } + aport->sample_size = sample_size; + aport->zero_word = zero_word; + aport->frame_size = aport->sw_channels * aport->sample_size; + aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift; + aport->hw_fragsize = 1 << aport->hw_fragshift; + aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift; + ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE); + ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE); + ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize)); + ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize)); + if (rport) { + int hwfrags, swfrags; + rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; + hwfrags = rport->hwbuf_max >> aport->hw_fragshift; + swfrags = aport->hw_fragcount - hwfrags; + if (swfrags < 2) + swfrags = 2; + rport->swbuf_size = swfrags * aport->hw_fragsize; + DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); + DBGPV("read hwbuf_max = %d, swbuf_size = %d\n", + rport->hwbuf_max, rport->swbuf_size); + } + if (wport) { + int hwfrags, swfrags; + int total_bytes = aport->hw_fragcount * aport->hw_fragsize; + wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE; + if (wport->hwbuf_max > total_bytes) + wport->hwbuf_max = total_bytes; + hwfrags = wport->hwbuf_max >> aport->hw_fragshift; + DBGPV("hwfrags = %d\n", hwfrags); + swfrags = aport->hw_fragcount - hwfrags; + if (swfrags < 2) + swfrags = 2; + wport->swbuf_size = swfrags * aport->hw_fragsize; + DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags); + DBGPV("write hwbuf_max = %d, swbuf_size = %d\n", + wport->hwbuf_max, wport->swbuf_size); + } + + aport->swb_u_idx = 0; + aport->swb_i_idx = 0; + aport->byte_count = 0; + + /* + * Is this a Cobalt bug? We need to make this buffer extend + * one page further than we actually use -- somehow memcpy + * causes an exceptoin otherwise. I suspect there's a bug in + * Cobalt (or somewhere) where it's generating a fault on a + * speculative load or something. Obviously, I haven't taken + * the time to track it down. + */ + + aport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); + if (!aport->swbuf) + return -ENOMEM; + if (rport && wport) { + ASSERT(aport == rport); + ASSERT(wport->swbuf == NULL); + /* One extra page - see comment above. */ + wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE); + if (!wport->swbuf) { + vfree(aport->swbuf); + aport->swbuf = NULL; + return -ENOMEM; + } + wport->sample_size = rport->sample_size; + wport->zero_word = rport->zero_word; + wport->frame_size = rport->frame_size; + wport->hw_fragshift = rport->hw_fragshift; + wport->hw_fragsize = rport->hw_fragsize; + wport->hw_fragcount = rport->hw_fragcount; + wport->swbuf_size = rport->swbuf_size; + wport->hwbuf_max = rport->hwbuf_max; + wport->swb_u_idx = rport->swb_u_idx; + wport->swb_i_idx = rport->swb_i_idx; + wport->byte_count = rport->byte_count; + } + if (rport) { + rport->swb_u_avail = 0; + rport->swb_i_avail = rport->swbuf_size; + rport->swstate = SW_RUN; + li_setup_dma(&rport->chan, + &li_comm1, + &devc->lith, + rport->hwbuf_paddr, + HWBUF_SHIFT, + rport->hw_fragshift, + rport->sw_channels, + rport->sample_size); + ad1843_setup_adc(&devc->lith, + rport->sw_framerate, + rport->sw_samplefmt, + rport->sw_channels); + li_enable_interrupts(&devc->lith, READ_INTR_MASK); + if (!(rport->flags & DISABLED)) { + ustmsc_t ustmsc; + rport->hwstate = HW_RUNNING; + li_activate_dma(&rport->chan); + li_read_USTMSC(&rport->chan, &ustmsc); + rport->MSC_offset = ustmsc.msc; + } + } + if (wport) { + if (wport->hwbuf_max > wport->swbuf_size) + wport->hwbuf_max = wport->swbuf_size; + wport->flags &= ~ERFLOWN; + wport->swb_u_avail = wport->swbuf_size; + wport->swb_i_avail = 0; + wport->swstate = SW_RUN; + li_setup_dma(&wport->chan, + &li_comm2, + &devc->lith, + wport->hwbuf_paddr, + HWBUF_SHIFT, + wport->hw_fragshift, + wport->sw_channels, + wport->sample_size); + ad1843_setup_dac(&devc->lith, + wport->sw_framerate, + wport->sw_samplefmt, + wport->sw_channels); + li_enable_interrupts(&devc->lith, WRITE_INTR_MASK); + } + DBGRV(); + return 0; +} + +/* + * pcm_shutdown_port - shut down one port (direction) for PCM I/O. + * Only called from pcm_shutdown. + */ + +static void pcm_shutdown_port(vwsnd_dev_t *devc, + vwsnd_port_t *aport, + unsigned int mask) +{ + unsigned long flags; + vwsnd_port_hwstate_t hwstate; + struct wait_queue wait = { current, NULL }; + + aport->swstate = SW_INITIAL; + add_wait_queue(&aport->queue, &wait); + current->state = TASK_UNINTERRUPTIBLE; + while (1) { + spin_lock_irqsave(&aport->lock, flags); + { + hwstate = aport->hwstate; + } + spin_unlock_irqrestore(&aport->lock, flags); + if (hwstate == HW_STOPPED) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&aport->queue, &wait); + li_disable_interrupts(&devc->lith, mask); + if (aport == &devc->rport) + ad1843_shutdown_adc(&devc->lith); + else /* aport == &devc->wport) */ + ad1843_shutdown_dac(&devc->lith); + li_shutdown_dma(&aport->chan); + vfree(aport->swbuf); + aport->swbuf = NULL; + aport->byte_count = 0; +} + +/* + * pcm_shutdown undoes what pcm_setup did. + * Also sets the ports' swstate to newstate. + */ + +static void pcm_shutdown(vwsnd_dev_t *devc, + vwsnd_port_t *rport, + vwsnd_port_t *wport) +{ + DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport); + + if (rport && rport->swbuf) { + DBGPV("shutting down rport\n"); + pcm_shutdown_port(devc, rport, READ_INTR_MASK); + } + if (wport && wport->swbuf) { + DBGPV("shutting down wport\n"); + pcm_shutdown_port(devc, wport, WRITE_INTR_MASK); + } + DBGRV(); +} + +static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb) +{ + char *src = rport->hwbuf + hwidx; + char *dst = rport->swbuf + swidx; + int fmt = rport->sw_samplefmt; + + DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx); + ASSERT(rport->hwbuf != NULL); + ASSERT(rport->swbuf != NULL); + ASSERT(nb > 0 && (nb % 32) == 0); + ASSERT(swidx % 32 == 0 && hwidx % 32 == 0); + ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size); + ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size); + + if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) { + + /* See Sample Format Notes above. */ + + char *end = src + nb; + while (src < end) + *dst++ = *src++ ^ 0x80; + } else + memcpy(dst, src, nb); +} + +static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb) +{ + char *src = wport->swbuf + swidx; + char *dst = wport->hwbuf + hwidx; + int fmt = wport->sw_samplefmt; + + ASSERT(nb > 0 && (nb % 32) == 0); + ASSERT(wport->hwbuf != NULL); + ASSERT(wport->swbuf != NULL); + ASSERT(swidx % 32 == 0 && hwidx % 32 == 0); + ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size); + ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size); + if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) { + + /* See Sample Format Notes above. */ + + char *end = src + nb; + while (src < end) + *dst++ = *src++ ^ 0x80; + } else + memcpy(dst, src, nb); +} + +/* + * pcm_output() is called both from baselevel and from interrupt level. + * This is where audio frames are copied into the hardware-accessible + * ring buffer. + * + * Locking note: The part of this routine that figures out what to do + * holds wport->lock. The longer part releases wport->lock, but sets + * wport->flags & HW_BUSY. Afterward, it reacquires wport->lock, and + * checks for more work to do. + * + * If another thread calls pcm_output() while HW_BUSY is set, it + * returns immediately, knowing that the thread that set HW_BUSY will + * look for more work to do before returning. + * + * This has the advantage that port->lock is held for several short + * periods instead of one long period. Also, when pcm_output is + * called from base level, it reenables interrupts. + */ + +static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb) +{ + vwsnd_port_t *wport = &devc->wport; + const int hwmax = wport->hwbuf_max; + const int hwsize = wport->hwbuf_size; + const int swsize = wport->swbuf_size; + const int fragsize = wport->hw_fragsize; + unsigned long iflags; + + DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb); + spin_lock_irqsave(&wport->lock, iflags); + if (erflown) + wport->flags |= ERFLOWN; + (void) __swb_inc_u(wport, nb); + if (wport->flags & HW_BUSY) { + spin_unlock_irqrestore(&wport->lock, iflags); + DBGPV("returning: HW BUSY\n"); + return; + } + if (wport->flags & DISABLED) { + spin_unlock_irqrestore(&wport->lock, iflags); + DBGPV("returning: DISABLED\n"); + return; + } + wport->flags |= HW_BUSY; + while (1) { + int swptr, hwptr, hw_avail, sw_avail, swidx; + vwsnd_port_hwstate_t hwstate = wport->hwstate; + vwsnd_port_swstate_t swstate = wport->swstate; + int hw_unavail; + ustmsc_t ustmsc; + + hwptr = li_read_hwptr(&wport->chan); + swptr = li_read_swptr(&wport->chan); + hw_unavail = (swptr - hwptr + hwsize) % hwsize; + hw_avail = (hwmax - hw_unavail) & -fragsize; + sw_avail = wport->swb_i_avail & -fragsize; + if (sw_avail && swstate == SW_RUN) { + if (wport->flags & ERFLOWN) { + wport->flags &= ~ERFLOWN; + } + } else if (swstate == SW_INITIAL || + swstate == SW_OFF || + (swstate == SW_DRAIN && + !sw_avail && + (wport->flags & ERFLOWN))) { + DBGP("stopping. hwstate = %d\n", hwstate); + if (hwstate != HW_STOPPED) { + li_deactivate_dma(&wport->chan); + wport->hwstate = HW_STOPPED; + } + wake_up(&wport->queue); + break; + } + if (!sw_avail || !hw_avail) + break; + spin_unlock_irqrestore(&wport->lock, iflags); + + /* + * We gave up the port lock, but we have the HW_BUSY flag. + * Proceed without accessing any nonlocal state. + * Do not exit the loop -- must check for more work. + */ + + swidx = wport->swb_i_idx; + nb = hw_avail; + if (nb > sw_avail) + nb = sw_avail; + if (nb > hwsize - swptr) + nb = hwsize - swptr; /* don't overflow hwbuf */ + if (nb > swsize - swidx) + nb = swsize - swidx; /* don't overflow swbuf */ + ASSERT(nb > 0); + if (nb % fragsize) { + DBGP("nb = %d, fragsize = %d\n", nb, fragsize); + DBGP("hw_avail = %d\n", hw_avail); + DBGP("sw_avail = %d\n", sw_avail); + DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr); + DBGP("swsize = %d, swidx = %d\n", swsize, swidx); + } + ASSERT(!(nb % fragsize)); + DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n", + swidx, swidx + nb, swptr, swptr + nb); + pcm_copy_out(wport, swidx, swptr, nb); + li_write_swptr(&wport->chan, (swptr + nb) % hwsize); + spin_lock_irqsave(&wport->lock, iflags); + if (hwstate == HW_STOPPED) { + DBGPV("starting\n"); + li_activate_dma(&wport->chan); + wport->hwstate = HW_RUNNING; + li_read_USTMSC(&wport->chan, &ustmsc); + ASSERT(wport->byte_count % wport->frame_size == 0); + wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size; + } + __swb_inc_i(wport, nb); + wport->byte_count += nb; + wport->frag_count += nb / fragsize; + ASSERT(nb % fragsize == 0); + wake_up(&wport->queue); + } + wport->flags &= ~HW_BUSY; + spin_unlock_irqrestore(&wport->lock, iflags); + DBGRV(); +} + +/* + * pcm_input() is called both from baselevel and from interrupt level. + * This is where audio frames are copied out of the hardware-accessible + * ring buffer. + * + * Locking note: The part of this routine that figures out what to do + * holds rport->lock. The longer part releases rport->lock, but sets + * rport->flags & HW_BUSY. Afterward, it reacquires rport->lock, and + * checks for more work to do. + * + * If another thread calls pcm_input() while HW_BUSY is set, it + * returns immediately, knowing that the thread that set HW_BUSY will + * look for more work to do before returning. + * + * This has the advantage that port->lock is held for several short + * periods instead of one long period. Also, when pcm_input is + * called from base level, it reenables interrupts. + */ + +static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb) +{ + vwsnd_port_t *rport = &devc->rport; + const int hwmax = rport->hwbuf_max; + const int hwsize = rport->hwbuf_size; + const int swsize = rport->swbuf_size; + const int fragsize = rport->hw_fragsize; + unsigned long iflags; + + DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb); + + spin_lock_irqsave(&rport->lock, iflags); + if (erflown) + rport->flags |= ERFLOWN; + (void) __swb_inc_u(rport, nb); + if (rport->flags & HW_BUSY || !rport->swbuf) { + spin_unlock_irqrestore(&rport->lock, iflags); + DBGPV("returning: HW BUSY or !swbuf\n"); + return; + } + if (rport->flags & DISABLED) { + spin_unlock_irqrestore(&rport->lock, iflags); + DBGPV("returning: DISABLED\n"); + return; + } + rport->flags |= HW_BUSY; + while (1) { + int swptr, hwptr, hw_avail, sw_avail, swidx; + vwsnd_port_hwstate_t hwstate = rport->hwstate; + vwsnd_port_swstate_t swstate = rport->swstate; + + hwptr = li_read_hwptr(&rport->chan); + swptr = li_read_swptr(&rport->chan); + hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize; + if (hw_avail > hwmax) + hw_avail = hwmax; + sw_avail = rport->swb_i_avail & -fragsize; + if (swstate != SW_RUN) { + DBGP("stopping. hwstate = %d\n", hwstate); + if (hwstate != HW_STOPPED) { + li_deactivate_dma(&rport->chan); + rport->hwstate = HW_STOPPED; + } + wake_up(&rport->queue); + break; + } + if (!sw_avail || !hw_avail) + break; + spin_unlock_irqrestore(&rport->lock, iflags); + + /* + * We gave up the port lock, but we have the HW_BUSY flag. + * Proceed without accessing any nonlocal state. + * Do not exit the loop -- must check for more work. + */ + + swidx = rport->swb_i_idx; + nb = hw_avail; + if (nb > sw_avail) + nb = sw_avail; + if (nb > hwsize - swptr) + nb = hwsize - swptr; /* don't overflow hwbuf */ + if (nb > swsize - swidx) + nb = swsize - swidx; /* don't overflow swbuf */ + ASSERT(nb > 0); + if (nb % fragsize) { + DBGP("nb = %d, fragsize = %d\n", nb, fragsize); + DBGP("hw_avail = %d\n", hw_avail); + DBGP("sw_avail = %d\n", sw_avail); + DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr); + DBGP("swsize = %d, swidx = %d\n", swsize, swidx); + } + ASSERT(!(nb % fragsize)); + DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n", + swptr, swptr + nb, swidx, swidx + nb); + pcm_copy_in(rport, swidx, swptr, nb); + li_write_swptr(&rport->chan, (swptr + nb) % hwsize); + spin_lock_irqsave(&rport->lock, iflags); + __swb_inc_i(rport, nb); + rport->byte_count += nb; + rport->frag_count += nb / fragsize; + ASSERT(nb % fragsize == 0); + wake_up(&rport->queue); + } + rport->flags &= ~HW_BUSY; + spin_unlock_irqrestore(&rport->lock, iflags); + DBGRV(); +} + +/* + * pcm_flush_frag() writes zero samples to fill the current fragment, + * then flushes it to the hardware. + * + * It is only meaningful to flush output, not input. + */ + +static void pcm_flush_frag(vwsnd_dev_t *devc) +{ + vwsnd_port_t *wport = &devc->wport; + + DBGPV("swstate = %d\n", wport->swstate); + if (wport->swstate == SW_RUN) { + int idx = wport->swb_u_idx; + int end = (idx + wport->hw_fragsize - 1) + >> wport->hw_fragshift + << wport->hw_fragshift; + int nb = end - idx; + DBGPV("clearing %d bytes\n", nb); + if (nb) + memset(wport->swbuf + idx, + (char) wport->zero_word, + nb); + wport->swstate = SW_DRAIN; + pcm_output(devc, 0, nb); + } + DBGRV(); +} + +/* + * Wait for output to drain. This sleeps uninterruptibly because + * there is nothing intelligent we can do if interrupted. This + * means the process will be delayed in responding to the signal. + */ + +static void pcm_write_sync(vwsnd_dev_t *devc) +{ + vwsnd_port_t *wport = &devc->wport; + struct wait_queue wait = { current, NULL }; + unsigned long flags; + vwsnd_port_hwstate_t hwstate; + + DBGEV("(devc=0x%p)\n", devc); + add_wait_queue(&wport->queue, &wait); + current->state = TASK_UNINTERRUPTIBLE; + while (1) { + spin_lock_irqsave(&wport->lock, flags); + { + hwstate = wport->hwstate; + } + spin_unlock_irqrestore(&wport->lock, flags); + if (hwstate == HW_STOPPED) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate); + DBGRV(); +} + +/*****************************************************************************/ +/* audio driver */ + +/* + * seek on an audio device always fails. + */ + +static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status) +{ + int overflown = status & LI_INTR_COMM1_OVERFLOW; + + if (status & READ_INTR_MASK) + pcm_input(devc, overflown, 0); +} + +static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status) +{ + int underflown = status & LI_INTR_COMM2_UNDERFLOW; + + if (status & WRITE_INTR_MASK) + pcm_output(devc, underflown, 0); +} + +static void vwsnd_audio_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) dev_id; + unsigned int status; + + DBGEV("(irq=%d, dev_id=0x%p, regs=0x%p)\n", irq, dev_id, regs); + + status = li_get_clear_intr_status(&devc->lith); + vwsnd_audio_read_intr(devc, status); + vwsnd_audio_write_intr(devc, status); +} + +static loff_t vwsnd_audio_llseek(struct file *file, loff_t offset, int whence) +{ + DBGEV("(file=0x%p, offset=%Ld, whence=%d)\n", file, offset, whence); + return -ESPIPE; +} + +static ssize_t vwsnd_audio_do_read(struct file *file, + char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ? + &devc->rport : NULL); + int ret, nb; + + DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n", + file, buffer, count, ppos); + + if (!rport) + return -EINVAL; + + if (rport->swbuf == NULL) { + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + ret = pcm_setup(devc, rport, wport); + if (ret < 0) + return ret; + } + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count) { + struct wait_queue wait = { current, NULL }; + add_wait_queue(&rport->queue, &wait); + current->state = TASK_INTERRUPTIBLE; + while ((nb = swb_inc_u(rport, 0)) == 0) { + DBGPV("blocking\n"); + if (rport->flags & DISABLED || + file->f_flags & O_NONBLOCK) { + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + return ret ? ret : -EAGAIN; + } + schedule(); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + return ret ? ret : -ERESTARTSYS; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&rport->queue, &wait); + pcm_input(devc, 0, 0); + /* nb bytes are available in userbuf. */ + if (nb > count) + nb = count; + DBGPV("nb = %d\n", nb); + copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb); + (void) swb_inc_u(rport, nb); + buffer += nb; + count -= nb; + ret += nb; + } + DBGPV("returning %d\n", ret); + return ret; +} + +static ssize_t vwsnd_audio_read(struct file *file, + char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + ssize_t ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_read(file, buffer, count, ppos); + up(&devc->io_sema); + return ret; +} + +static ssize_t vwsnd_audio_do_write(struct file *file, + const char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL); + int ret, nb; + + DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n", + file, buffer, count, ppos); + + if (!wport) + return -EINVAL; + + if (wport->swbuf == NULL) { + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + ret = pcm_setup(devc, rport, wport); + if (ret < 0) + return ret; + } + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count) { + struct wait_queue wait = { current, NULL }; + add_wait_queue(&wport->queue, &wait); + current->state = TASK_INTERRUPTIBLE; + while ((nb = swb_inc_u(wport, 0)) == 0) { + if (wport->flags & DISABLED || + file->f_flags & O_NONBLOCK) { + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + return ret ? ret : -EAGAIN; + } + schedule(); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + return ret ? ret : -ERESTARTSYS; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&wport->queue, &wait); + /* nb bytes are available in userbuf. */ + if (nb > count) + nb = count; + DBGPV("nb = %d\n", nb); + copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb); + pcm_output(devc, 0, nb); + buffer += nb; + count -= nb; + ret += nb; + } + DBGPV("returning %d\n", ret); + return ret; +} + +static ssize_t vwsnd_audio_write(struct file *file, + const char *buffer, + size_t count, + loff_t *ppos) +{ + vwsnd_dev_t *devc = file->private_data; + ssize_t ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_write(file, buffer, count, ppos); + up(&devc->io_sema); + return ret; +} + +static unsigned int vwsnd_audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + unsigned int mask = 0; + + DBGEV("(file=0x%p, wait=0x%p)\n", file, wait); + + ASSERT(rport || wport); + if (rport) { + poll_wait(file, &rport->queue, wait); + if (swb_inc_u(rport, 0)) + mask |= (POLLIN | POLLRDNORM); + } + if (wport) { + poll_wait(file, &wport->queue, wait); + if (wport->swbuf == NULL || swb_inc_u(wport, 0)) + mask |= (POLLOUT | POLLWRNORM); + } + + DBGPV("returning 0x%x\n", mask); + return mask; +} + +static int vwsnd_audio_do_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ? + &devc->rport : NULL; + vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ? + &devc->wport : NULL; + vwsnd_port_t *aport = rport ? rport : wport; + struct audio_buf_info buf_info; + struct count_info info; + unsigned long flags; + int ival; + + + DBGEV("(inode=0x%p, file=0x%p, cmd=0x%x, arg=0x%lx)\n", + inode, file, cmd, arg); + switch (cmd) { + case OSS_GETVERSION: /* _SIOR ('M', 118, int) */ + DBGX("OSS_GETVERSION\n"); + ival = SOUND_VERSION; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETCAPS: /* _SIOR ('P',15, int) */ + DBGX("SNDCTL_DSP_GETCAPS\n"); + ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETFMTS: /* _SIOR ('P',11, int) */ + DBGX("SNDCTL_DSP_GETFMTS\n"); + ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | + AFMT_U8 | AFMT_S8); + return put_user(ival, (int *) arg); + break; + + case SOUND_PCM_READ_RATE: /* _SIOR ('P', 2, int) */ + DBGX("SOUND_PCM_READ_RATE\n"); + ival = aport->sw_framerate; + return put_user(ival, (int *) arg); + + case SOUND_PCM_READ_CHANNELS: /* _SIOR ('P', 6, int) */ + DBGX("SOUND_PCM_READ_CHANNELS\n"); + ival = aport->sw_channels; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SPEED: /* _SIOWR('P', 2, int) */ + get_user_ret(ival, (int *) arg, -EFAULT); + DBGX("SNDCTL_DSP_SPEED %d\n", ival); + if (ival) { + if (aport->swstate != SW_INITIAL) { + DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n", + aport->swstate); + return -EINVAL; + } + if (ival < MIN_SPEED) + ival = MIN_SPEED; + if (ival > MAX_SPEED) + ival = MAX_SPEED; + if (rport) + rport->sw_framerate = ival; + if (wport) + wport->sw_framerate = ival; + } else + ival = aport->sw_framerate; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_STEREO: /* _SIOWR('P', 3, int) */ + get_user_ret(ival, (int *) arg, -EFAULT); + DBGX("SNDCTL_DSP_STEREO %d\n", ival); + if (ival != 0 && ival != 1) + return -EINVAL; + if (aport->swstate != SW_INITIAL) + return -EINVAL; + if (rport) + rport->sw_channels = ival + 1; + if (wport) + wport->sw_channels = ival + 1; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_CHANNELS: /* _SIOWR('P', 6, int) */ + get_user_ret(ival, (int *) arg, -EFAULT); + DBGX("SNDCTL_DSP_CHANNELS %d\n", ival); + if (ival != 1 && ival != 2) + return -EINVAL; + if (aport->swstate != SW_INITIAL) + return -EINVAL; + if (rport) + rport->sw_channels = ival; + if (wport) + wport->sw_channels = ival; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETBLKSIZE: /* _SIOWR('P', 4, int) */ + ival = pcm_setup(devc, rport, wport); + if (ival < 0) { + DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival); + return ival; + } + ival = 1 << aport->sw_fragshift; + DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SETFRAGMENT: /* _SIOWR('P',10, int) */ + get_user_ret(ival, (int *) arg, -EFAULT); + DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n", + ival >> 16, ival & 0xFFFF); + if (aport->swstate != SW_INITIAL) + return -EINVAL; + { + int sw_fragshift = ival & 0xFFFF; + int sw_subdivshift = aport->sw_subdivshift; + int hw_fragshift = sw_fragshift - sw_subdivshift; + int sw_fragcount = (ival >> 16) & 0xFFFF; + int hw_fragsize; + if (hw_fragshift < MIN_FRAGSHIFT) + hw_fragshift = MIN_FRAGSHIFT; + if (hw_fragshift > MAX_FRAGSHIFT) + hw_fragshift = MAX_FRAGSHIFT; + sw_fragshift = hw_fragshift + aport->sw_subdivshift; + hw_fragsize = 1 << hw_fragshift; + if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize)) + sw_fragcount = MIN_FRAGCOUNT(hw_fragsize); + if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize)) + sw_fragcount = MAX_FRAGCOUNT(hw_fragsize); + DBGPV("sw_fragshift = %d\n", sw_fragshift); + DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport); + if (rport) { + rport->sw_fragshift = sw_fragshift; + rport->sw_fragcount = sw_fragcount; + } + if (wport) { + wport->sw_fragshift = sw_fragshift; + wport->sw_fragcount = sw_fragcount; + } + ival = sw_fragcount << 16 | sw_fragshift; + } + DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n", + ival >> 16, ival & 0xFFFF); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SUBDIVIDE: /* _SIOWR('P', 9, int) */ + get_user_ret(ival, (int *) arg, -EFAULT); + DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival); + if (aport->swstate != SW_INITIAL) + return -EINVAL; + { + int subdivshift; + int hw_fragshift, hw_fragsize, hw_fragcount; + switch (ival) { + case 1: subdivshift = 0; break; + case 2: subdivshift = 1; break; + case 4: subdivshift = 2; break; + default: return -EINVAL; + } + hw_fragshift = aport->sw_fragshift - subdivshift; + if (hw_fragshift < MIN_FRAGSHIFT || + hw_fragshift > MAX_FRAGSHIFT) + return -EINVAL; + hw_fragsize = 1 << hw_fragshift; + hw_fragcount = aport->sw_fragcount >> subdivshift; + if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) || + hw_fragcount > MAX_FRAGCOUNT(hw_fragsize)) + return -EINVAL; + if (rport) + rport->sw_subdivshift = subdivshift; + if (wport) + wport->sw_subdivshift = subdivshift; + } + return 0; + + case SNDCTL_DSP_SETFMT: /* _SIOWR('P',5, int) */ + get_user_ret(ival, (int *) arg, -EFAULT); + DBGX("SNDCTL_DSP_SETFMT %d\n", ival); + if (ival != AFMT_QUERY) { + if (aport->swstate != SW_INITIAL) { + DBGP("SETFMT failed, swstate = %d\n", + aport->swstate); + return -EINVAL; + } + switch (ival) { + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + case AFMT_S16_LE: + if (rport) + rport->sw_samplefmt = ival; + if (wport) + wport->sw_samplefmt = ival; + break; + default: + return -EINVAL; + } + } + ival = aport->sw_samplefmt; + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_GETOSPACE: /* _SIOR ('P',12, audio_buf_info) */ + DBGXV("SNDCTL_DSP_GETOSPACE\n"); + if (!wport) + return -EINVAL; + ival = pcm_setup(devc, rport, wport); + if (ival < 0) + return ival; + ival = swb_inc_u(wport, 0); + buf_info.fragments = ival >> wport->sw_fragshift; + buf_info.fragstotal = wport->sw_fragcount; + buf_info.fragsize = 1 << wport->sw_fragshift; + buf_info.bytes = ival; + DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n", + buf_info.fragments, buf_info.fragstotal, + buf_info.fragsize, buf_info.bytes); + return copy_to_user((void *) arg, &buf_info, sizeof buf_info); + + case SNDCTL_DSP_GETISPACE: /* _SIOR ('P',13, audio_buf_info) */ + DBGX("SNDCTL_DSP_GETISPACE\n"); + if (!rport) + return -EINVAL; + ival = pcm_setup(devc, rport, wport); + if (ival < 0) + return ival; + ival = swb_inc_u(rport, 0); + buf_info.fragments = ival >> rport->sw_fragshift; + buf_info.fragstotal = rport->sw_fragcount; + buf_info.fragsize = 1 << rport->sw_fragshift; + buf_info.bytes = ival; + DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n", + buf_info.fragments, buf_info.fragstotal, + buf_info.fragsize, buf_info.bytes); + return copy_to_user((void *) arg, &buf_info, sizeof buf_info); + + case SNDCTL_DSP_NONBLOCK: /* _SIO ('P',14) */ + DBGX("SNDCTL_DSP_NONBLOCK\n"); + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: /* _SIO ('P', 0) */ + DBGX("SNDCTL_DSP_RESET\n"); + /* + * Nothing special needs to be done for input. Input + * samples sit in swbuf, but it will be reinitialized + * to empty when pcm_setup() is called. + */ + if (wport && wport->swbuf) { + wport->swstate = SW_INITIAL; + pcm_output(devc, 0, 0); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + return 0; + + case SNDCTL_DSP_SYNC: /* _SIO ('P', 1) */ + DBGX("SNDCTL_DSP_SYNC\n"); + if (wport) { + pcm_flush_frag(devc); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + return 0; + + case SNDCTL_DSP_POST: /* _SIO ('P', 8) */ + DBGX("SNDCTL_DSP_POST\n"); + if (!wport) + return -EINVAL; + pcm_flush_frag(devc); + return 0; + + case SNDCTL_DSP_GETIPTR: /* _SIOR ('P', 17, count_info) */ + DBGX("SNDCTL_DSP_GETIPTR\n"); + if (!rport) + return -EINVAL; + spin_lock_irqsave(&rport->lock, flags); + { + ustmsc_t ustmsc; + if (rport->hwstate == HW_RUNNING) { + ASSERT(rport->swstate == SW_RUN); + li_read_USTMSC(&rport->chan, &ustmsc); + info.bytes = ustmsc.msc - rport->MSC_offset; + info.bytes *= rport->frame_size; + } else { + info.bytes = rport->byte_count; + } + info.blocks = rport->frag_count; + info.ptr = 0; /* not implemented */ + rport->frag_count = 0; + } + spin_unlock_irqrestore(&rport->lock, flags); + return copy_to_user((void *) arg, &info, sizeof info); + + case SNDCTL_DSP_GETOPTR: /* _SIOR ('P',18, count_info) */ + DBGX("SNDCTL_DSP_GETOPTR\n"); + if (!wport) + return -EINVAL; + spin_lock_irqsave(&wport->lock, flags); + { + ustmsc_t ustmsc; + if (wport->hwstate == HW_RUNNING) { + ASSERT(wport->swstate == SW_RUN); + li_read_USTMSC(&wport->chan, &ustmsc); + info.bytes = ustmsc.msc - wport->MSC_offset; + info.bytes *= wport->frame_size; + } else { + info.bytes = wport->byte_count; + } + info.blocks = wport->frag_count; + info.ptr = 0; /* not implemented */ + wport->frag_count = 0; + } + spin_unlock_irqrestore(&wport->lock, flags); + return copy_to_user((void *) arg, &info, sizeof info); + + case SNDCTL_DSP_GETODELAY: /* _SIOR ('P', 23, int) */ + DBGX("SNDCTL_DSP_GETODELAY\n"); + if (!wport) + return -EINVAL; + spin_lock_irqsave(&wport->lock, flags); + { + int fsize = wport->frame_size; + ival = wport->swb_i_avail / fsize; + if (wport->hwstate == HW_RUNNING) { + int swptr, hwptr, hwframes, hwbytes, hwsize; + int totalhwbytes; + ustmsc_t ustmsc; + + hwsize = wport->hwbuf_size; + swptr = li_read_swptr(&wport->chan); + li_read_USTMSC(&wport->chan, &ustmsc); + hwframes = ustmsc.msc - wport->MSC_offset; + totalhwbytes = hwframes * fsize; + hwptr = totalhwbytes % hwsize; + hwbytes = (swptr - hwptr + hwsize) % hwsize; + ival += hwbytes / fsize; + } + } + spin_unlock_irqrestore(&wport->lock, flags); + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_PROFILE: /* _SIOW ('P', 23, int) */ + DBGX("SNDCTL_DSP_PROFILE\n"); + + /* + * Thomas Sailer explains SNDCTL_DSP_PROFILE + * (private email, March 24, 1999): + * + * This gives the sound driver a hint on what it + * should do with partial fragments + * (i.e. fragments partially filled with write). + * This can direct the driver to zero them or + * leave them alone. But don't ask me what this + * is good for, my driver just zeroes the last + * fragment before the receiver stops, no idea + * what good for any other behaviour could + * be. Implementing it as NOP seems safe. + */ + + break; + + case SNDCTL_DSP_GETTRIGGER: /* _SIOR ('P',16, int) */ + DBGX("SNDCTL_DSP_GETTRIGGER\n"); + ival = 0; + if (rport) { + spin_lock_irqsave(&rport->lock, flags); + { + if (!(rport->flags & DISABLED)) + ival |= PCM_ENABLE_INPUT; + } + spin_unlock_irqrestore(&rport->lock, flags); + } + if (wport) { + spin_lock_irqsave(&wport->lock, flags); + { + if (!(wport->flags & DISABLED)) + ival |= PCM_ENABLE_OUTPUT; + } + spin_unlock_irqrestore(&wport->lock, flags); + } + return put_user(ival, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: /* _SIOW ('P',16, int) */ + get_user_ret(ival, (int *) arg, -EFAULT); + DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival); + + /* + * If user is disabling I/O and port is not in initial + * state, fail with EINVAL. + */ + + if (((rport && !(ival & PCM_ENABLE_INPUT)) || + (wport && !(ival & PCM_ENABLE_OUTPUT))) && + aport->swstate != SW_INITIAL) + return -EINVAL; + + if (rport) { + vwsnd_port_hwstate_t hwstate; + spin_lock_irqsave(&rport->lock, flags); + { + hwstate = rport->hwstate; + if (ival & PCM_ENABLE_INPUT) + rport->flags &= ~DISABLED; + else + rport->flags |= DISABLED; + } + spin_unlock_irqrestore(&rport->lock, flags); + if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) { + + if (rport->swstate == SW_INITIAL) + pcm_setup(devc, rport, wport); + else + li_activate_dma(&rport->chan); + } + } + if (wport) { + vwsnd_port_flags_t pflags; + spin_lock_irqsave(&wport->lock, flags); + { + pflags = wport->flags; + if (ival & PCM_ENABLE_OUTPUT) + wport->flags &= ~DISABLED; + else + wport->flags |= DISABLED; + } + spin_unlock_irqrestore(&wport->lock, flags); + if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) { + if (wport->swstate == SW_RUN) + pcm_output(devc, 0, 0); + } + } + return 0; + + default: + DBGP("unknown ioctl 0x%x\n", cmd); + return -EINVAL; + } + DBGP("unimplemented ioctl 0x%x\n", cmd); + return -EINVAL; +} + +static int vwsnd_audio_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + int ret; + + down(&devc->io_sema); + ret = vwsnd_audio_do_ioctl(inode, file, cmd, arg); + up(&devc->io_sema); + return ret; +} + +/* No mmap. */ + +static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + DBGE("(file=0x%p, vma=0x%p)\n", file, vma); + return -ENODEV; +} + +/* + * Open the audio device for read and/or write. + * + * Returns 0 on success, -errno on failure. + */ + +static int vwsnd_audio_open(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc; + dev_t minor = MINOR(inode->i_rdev); + int sw_samplefmt; + + DBGE("(inode=0x%p, file=0x%p)\n", inode, file); + + INC_USE_COUNT; + for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) + if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F)) + break; + + if (devc == NULL) { + DEC_USE_COUNT; + return -ENODEV; + } + + down(&devc->open_sema); + while (devc->open_mode & file->f_mode) { + up(&devc->open_sema); + if (file->f_flags & O_NONBLOCK) { + DEC_USE_COUNT; + return -EBUSY; + } + interruptible_sleep_on(&devc->open_wait); + if (signal_pending(current)) { + DEC_USE_COUNT; + return -ERESTARTSYS; + } + down(&devc->open_sema); + } + devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&devc->open_sema); + + /* get default sample format from minor number. */ + + sw_samplefmt = 0; + if ((minor & 0xF) == SND_DEV_DSP) + sw_samplefmt = AFMT_U8; + else if ((minor & 0xF) == SND_DEV_AUDIO) + sw_samplefmt = AFMT_MU_LAW; + else if ((minor & 0xF) == SND_DEV_DSP16) + sw_samplefmt = AFMT_S16_LE; + else + ASSERT(0); + + /* Initialize vwsnd_ports. */ + + down(&devc->io_sema); + { + if (file->f_mode & FMODE_READ) { + devc->rport.swstate = SW_INITIAL; + devc->rport.flags = 0; + devc->rport.sw_channels = 1; + devc->rport.sw_samplefmt = sw_samplefmt; + devc->rport.sw_framerate = 8000; + devc->rport.sw_fragshift = DEFAULT_FRAGSHIFT; + devc->rport.sw_fragcount = DEFAULT_FRAGCOUNT; + devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT; + devc->rport.byte_count = 0; + devc->rport.frag_count = 0; + } + if (file->f_mode & FMODE_WRITE) { + devc->wport.swstate = SW_INITIAL; + devc->wport.flags = 0; + devc->wport.sw_channels = 1; + devc->wport.sw_samplefmt = sw_samplefmt; + devc->wport.sw_framerate = 8000; + devc->wport.sw_fragshift = DEFAULT_FRAGSHIFT; + devc->wport.sw_fragcount = DEFAULT_FRAGCOUNT; + devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT; + devc->wport.byte_count = 0; + devc->wport.frag_count = 0; + } + } + up(&devc->io_sema); + + file->private_data = devc; + DBGRV(); + return 0; +} + +/* + * Release (close) the audio device. + */ + +static int vwsnd_audio_release(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + vwsnd_port_t *wport = NULL, *rport = NULL; + int err = 0; + + down(&devc->io_sema); + { + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + + if (file->f_mode & FMODE_READ) + rport = &devc->rport; + if (file->f_mode & FMODE_WRITE) { + wport = &devc->wport; + pcm_flush_frag(devc); + pcm_write_sync(devc); + } + pcm_shutdown(devc, rport, wport); + if (rport) + rport->swstate = SW_OFF; + if (wport) + wport->swstate = SW_OFF; + } + up(&devc->io_sema); + + down(&devc->open_sema); + { + devc->open_mode &= ~file->f_mode; + } + up(&devc->open_sema); + wake_up(&devc->open_wait); + DBGDO(if (IN_USE)) /* see hack in vwsnd_mixer_release() */ + DEC_USE_COUNT; + DBGR(); + return err; +} + +static struct file_operations vwsnd_audio_fops = { + &vwsnd_audio_llseek, + &vwsnd_audio_read, + &vwsnd_audio_write, + NULL, /* readdir */ + &vwsnd_audio_poll, + &vwsnd_audio_ioctl, + &vwsnd_audio_mmap, + &vwsnd_audio_open, + NULL, /* flush */ + &vwsnd_audio_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/*****************************************************************************/ +/* mixer driver */ + +/* open the mixer device. */ + +static int vwsnd_mixer_open(struct inode *inode, struct file *file) +{ + vwsnd_dev_t *devc; + + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + + INC_USE_COUNT; + for (devc = vwsnd_dev_list; devc; devc = devc->next_dev) + if (devc->mixer_minor == MINOR(inode->i_rdev)) + break; + + if (devc == NULL) { + DEC_USE_COUNT; + return -ENODEV; + } + file->private_data = devc; + return 0; +} + +/* release (close) the mixer device. */ + +static int vwsnd_mixer_release(struct inode *inode, struct file *file) +{ + DBGEV("(inode=0x%p, file=0x%p)\n", inode, file); + + /* + * hack -- opening/closing the mixer device zeroes use count + * so driver can be unloaded. + * Use only while debugging module, and then use it carefully. + */ + + DBGDO(while (IN_USE)) + DEC_USE_COUNT; + return 0; +} + +/* seek is illegal on mixer. */ + +static loff_t vwsnd_mixer_llseek(struct file *file, loff_t offset, int whence) +{ + return -ESPIPE; +} + +/* mixer_read_ioctl handles all read ioctls on the mixer device. */ + +static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg) +{ + int val = -1; + + DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg); + + switch (nr) { + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + case SOUND_MIXER_DEVMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV); + break; + + case SOUND_MIXER_STEREODEVS: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV); + break; + + case SOUND_MIXER_OUTMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD); + break; + + case SOUND_MIXER_RECMASK: + val = (SOUND_MASK_PCM | SOUND_MASK_LINE | + SOUND_MASK_MIC | SOUND_MASK_CD); + break; + + case SOUND_MIXER_PCM: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM); + break; + + case SOUND_MIXER_LINE: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE); + break; + + case SOUND_MIXER_MIC: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC); + break; + + case SOUND_MIXER_CD: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD); + break; + + case SOUND_MIXER_RECLEV: + val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV); + break; + + case SOUND_MIXER_RECSRC: + val = ad1843_get_recsrc(&devc->lith); + break; + + case SOUND_MIXER_OUTSRC: + val = ad1843_get_outsrc(&devc->lith); + break; + + default: + return -EINVAL; + } + return put_user(val, (int *) arg); +} + +/* mixer_write_ioctl handles all write ioctls on the mixer device. */ + +static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, caddr_t arg) +{ + int val; + int err; + + DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg); + + err = get_user(val, (int *) arg); + if (err) + return -EFAULT; + switch (nr) { + case SOUND_MIXER_PCM: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val); + break; + + case SOUND_MIXER_LINE: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val); + break; + + case SOUND_MIXER_MIC: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val); + break; + + case SOUND_MIXER_CD: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val); + break; + + case SOUND_MIXER_RECLEV: + val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val); + break; + + case SOUND_MIXER_RECSRC: + if (devc->rport.swbuf || devc->wport.swbuf) + return -EBUSY; /* can't change recsrc while running */ + val = ad1843_set_recsrc(&devc->lith, val); + break; + + case SOUND_MIXER_OUTSRC: + val = ad1843_set_outsrc(&devc->lith, val); + break; + + default: + return -EINVAL; + } + if (val < 0) + return val; + return put_user(val, (int *) arg); +} + +/* This is the ioctl entry to the mixer driver. */ + +static int vwsnd_mixer_ioctl(struct inode *ioctl, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data; + const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT; + const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT; + int retval; + + DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg); + + down(&devc->mix_sema); + { + if ((cmd & ~nrmask) == MIXER_READ(0)) + retval = mixer_read_ioctl(devc, nr, (caddr_t) arg); + else if ((cmd & ~nrmask) == MIXER_WRITE(0)) + retval = mixer_write_ioctl(devc, nr, (caddr_t) arg); + else + retval = -EINVAL; + } + up(&devc->mix_sema); + return retval; +} + +static struct file_operations vwsnd_mixer_fops = { + &vwsnd_mixer_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &vwsnd_mixer_ioctl, + NULL, /* mmap */ + &vwsnd_mixer_open, + NULL, /* flush */ + &vwsnd_mixer_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/*****************************************************************************/ +/* probe/attach/unload */ + +/* driver probe routine. Return nonzero if hardware is found. */ + +static int probe_vwsnd(struct address_info *hw_config) +{ + lithium_t lith; + int w; + unsigned long later; + + DBGEV("(hw_config=0x%p)\n", hw_config); + + /* XXX verify lithium present (to prevent crash on non-vw) */ + + if (li_create(&lith, hw_config->io_base) != 0) { + printk(KERN_WARNING "probe_vwsnd: can't map lithium\n"); + return 0; + } + later = jiffies + 2; + li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE); + do { + w = li_readl(&lith, LI_HOST_CONTROLLER); + } while (w == LI_HC_LINK_ENABLE && jiffies < later); + + li_destroy(&lith); + + DBGPV("HC = 0x%04x\n", w); + + if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) { + + /* This may indicate a beta machine with no audio, + * or a future machine with different audio. + * On beta-release 320 w/ no audio, HC == 0x4000 */ + + printk(KERN_WARNING "probe_vwsnd: audio codec not found\n"); + return 0; + } + + if (w & LI_HC_LINK_FAILURE) { + printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n"); + return 0; + } + + printk(KERN_INFO "probe_vwsnd: lithium audio found\n"); + + return 1; +} + +/* + * driver attach routine. Initialize driver data structures and + * initialize hardware. A new vwsnd_dev_t is allocated and put + * onto the global list, vwsnd_dev_list. + * + * Return +minor_dev on success, -errno on failure. + */ + +static int attach_vwsnd(struct address_info *hw_config) +{ + vwsnd_dev_t *devc = NULL; + int err = -ENOMEM; + + DBGEV("(hw_config=0x%p)\n", hw_config); + + devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL); + if (devc == NULL) + goto fail0; + + err = li_create(&devc->lith, hw_config->io_base); + if (err) + goto fail1; + + init_waitqueue(&devc->open_wait); + + devc->rport.hwbuf_size = HWBUF_SIZE; + devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER); + if (!devc->rport.hwbuf_vaddr) + goto fail2; + devc->rport.hwbuf = (caddr_t) devc->rport.hwbuf_vaddr; + devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf); + + /* + * Quote from the NT driver: + * + * // WARNING!!! HACK to setup output dma!!! + * // This is required because even on output there is some data + * // trickling into the input DMA channel. This is a bug in the + * // Lithium microcode. + * // --sde + * + * We set the input side's DMA base address here. It will remain + * valid until the driver is unloaded. + */ + + li_writel(&devc->lith, LI_COMM1_BASE, + devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8)); + + devc->wport.hwbuf_size = HWBUF_SIZE; + devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER); + if (!devc->wport.hwbuf_vaddr) + goto fail3; + devc->wport.hwbuf = (caddr_t) devc->wport.hwbuf_vaddr; + devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf); + DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf); + + DBGDO(shut_up++); + err = ad1843_init(&devc->lith); + DBGDO(shut_up--); + if (err) + goto fail4; + + /* install interrupt handler */ + + err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc); + if (err) + goto fail5; + + /* register this device's drivers. */ + + devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1); + if ((err = devc->audio_minor) < 0) { + DBGDO(printk(KERN_WARNING + "attach_vwsnd: register_sound_dsp error %d\n", + err)); + goto fail6; + } + devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops, + devc->audio_minor >> 4); + if ((err = devc->mixer_minor) < 0) { + DBGDO(printk(KERN_WARNING + "attach_vwsnd: register_sound_mixer error %d\n", + err)); + goto fail7; + } + + /* Squirrel away device indices for unload routine. */ + + hw_config->slots[0] = devc->audio_minor; + + /* Initialize as much of *devc as possible */ + + devc->open_sema = MUTEX; + devc->io_sema = MUTEX; + devc->mix_sema = MUTEX; + devc->open_mode = 0; + devc->rport.lock = SPIN_LOCK_UNLOCKED; + init_waitqueue(&devc->rport.queue); + devc->rport.swstate = SW_OFF; + devc->rport.hwstate = HW_STOPPED; + devc->rport.flags = 0; + devc->rport.swbuf = NULL; + devc->wport.lock = SPIN_LOCK_UNLOCKED; + init_waitqueue(&devc->wport.queue); + devc->wport.swstate = SW_OFF; + devc->wport.hwstate = HW_STOPPED; + devc->wport.flags = 0; + devc->wport.swbuf = NULL; + + /* Success. Link us onto the local device list. */ + + devc->next_dev = vwsnd_dev_list; + vwsnd_dev_list = devc; + return devc->audio_minor; + + /* So many ways to fail. Undo what we did. */ + + fail7: + unregister_sound_dsp(devc->audio_minor); + fail6: + free_irq(hw_config->irq, devc); + fail5: + fail4: + free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER); + fail3: + free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER); + fail2: + li_destroy(&devc->lith); + fail1: + kfree(devc); + fail0: + return err; +} + +static int unload_vwsnd(struct address_info *hw_config) +{ + vwsnd_dev_t *devc, **devcp; + + DBGE("()\n"); + + if (IN_USE) + return -EBUSY; + devcp = &vwsnd_dev_list; + while ((devc = *devcp)) { + if (devc->audio_minor == hw_config->slots[0]) { + *devcp = devc->next_dev; + break; + } + devcp = &devc->next_dev; + } + + if (!devc) + return -ENODEV; + + unregister_sound_mixer(devc->mixer_minor); + unregister_sound_dsp(devc->audio_minor); + free_irq(hw_config->irq, devc); + free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER); + free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER); + li_destroy(&devc->lith); + kfree(devc); + + return 0; +} + +/*****************************************************************************/ +/* initialization and loadable kernel module interface */ + +static struct address_info the_hw_config = { + 0xFF001000, /* lithium phys addr */ + CO_IRQ(CO_APIC_LI_AUDIO) /* irq */ +}; + +#ifdef MODULE + +MODULE_DESCRIPTION("SGI Visual Workstation sound module"); +MODULE_AUTHOR("Bob Miller "); + +extern int init_module(void) +{ + int err; + + DBGXV("\n"); + DBGXV("sound::vwsnd::init_module()\n"); + + if(!probe_vwsnd(&the_hw_config)) + return -ENODEV; + err = attach_vwsnd(&the_hw_config); + if (err < 0) + return err; + return 0; +} + +extern void cleanup_module(void) +{ + DBGX("sound::vwsnd::cleanup_module()\n"); + + unload_vwsnd(&the_hw_config); +} + +#else + +extern void init_vwsnd(void) +{ + DBGX("sound::vwsnd::init_vwsnd()\n"); + if (probe_vwsnd(&the_hw_config)) + (void) attach_vwsnd(&the_hw_config); +} + +#endif /* !MODULE */ + +/* + * Local variables: + * compile-command: "cd ../..; make modules SUBDIRS=drivers/sound" + * c-basic-offset: 8 + * End: + */ diff -u --recursive --new-file v2.2.11/linux/drivers/video/Config.in linux/drivers/video/Config.in --- v2.2.11/linux/drivers/video/Config.in Mon Aug 9 16:05:57 1999 +++ linux/drivers/video/Config.in Wed Aug 25 17:29:49 1999 @@ -79,6 +79,7 @@ fi if [ "$CONFIG_VISWS" = "y" ]; then tristate 'SGI Visual Workstation framebuffer support' CONFIG_FB_SGIVW + define_bool CONFIG_BUS_I2C y fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_PCI" != "n" ]; then diff -u --recursive --new-file v2.2.11/linux/drivers/video/Makefile linux/drivers/video/Makefile --- v2.2.11/linux/drivers/video/Makefile Mon Aug 9 16:05:57 1999 +++ linux/drivers/video/Makefile Wed Aug 25 17:29:49 1999 @@ -141,10 +141,10 @@ endif ifeq ($(CONFIG_FB_SGIVW),y) -L_OBJS += sgivwfb.o +LX_OBJS += sgivwfb.o else ifeq ($(CONFIG_FB_SGIVW),m) - M_OBJS += sgivwfb.o + MX_OBJS += sgivwfb.o endif endif diff -u --recursive --new-file v2.2.11/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c --- v2.2.11/linux/drivers/video/fbcon.c Mon Aug 9 16:05:57 1999 +++ linux/drivers/video/fbcon.c Wed Aug 25 17:29:49 1999 @@ -215,7 +215,7 @@ #ifdef CONFIG_MAC /* - * On the Macintoy, there may or may not be a working VBL int. We need to prob + * On the Macintoy, there may or may not be a working VBL int. We need to probe */ static int vbl_detected = 0; @@ -1386,14 +1386,6 @@ if (!p->can_soft_blank) { if (blank) { -#ifdef CONFIG_MAC - if (MACH_IS_MAC) { - if (p->screen_base) - mymemset(p->screen_base, - p->var.xres_virtual*p->var.yres_virtual* - p->var.bits_per_pixel>>3); - } else -#endif if (p->visual == FB_VISUAL_MONO01) { if (p->screen_base) mymemset(p->screen_base, @@ -2228,12 +2220,13 @@ p->type == FB_TYPE_INTERLEAVED_PLANES)) { /* monochrome */ - unsigned char inverse = p->inverse ? 0x00 : 0xff; + unsigned char inverse = p->inverse || p->visual == FB_VISUAL_MONO01 + ? 0x00 : 0xff; /* can't use simply memcpy because need to apply inverse */ for( y1 = 0; y1 < LOGO_H; y1++ ) { - src = logo + y1*LOGO_LINE + x/8; - dst = fb + y1*line; + src = logo + y1*LOGO_LINE; + dst = fb + y1*line + x/8; for( x1 = 0; x1 < LOGO_LINE; ++x1 ) *dst++ = *src++ ^ inverse; } diff -u --recursive --new-file v2.2.11/linux/drivers/video/font_6x11.c linux/drivers/video/font_6x11.c --- v2.2.11/linux/drivers/video/font_6x11.c Tue Sep 29 20:56:33 1998 +++ linux/drivers/video/font_6x11.c Wed Aug 25 17:29:49 1999 @@ -1207,6 +1207,8 @@ 0x00, /* 00000000 */ /* 92 0x5c '\' */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ 0x20, /* 00 00000 */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ @@ -1215,8 +1217,6 @@ 0x08, /* 0000 000 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ - 0x02, /* 000000 0 */ - 0x02, /* 000000 0 */ 0x00, /* 00000000 */ /* 93 0x5d ']' */ @@ -1253,7 +1253,7 @@ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ + 0xfc, /* 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ @@ -2222,9 +2222,9 @@ /* 170 0xaa '\252' */ 0x00, /* 00000000 */ - 0x7a, /* 0 0 0 */ - 0x2e, /* 00 0 0 */ - 0x2e, /* 00 0 0 */ + 0xf4, /* 0 00 */ + 0x5c, /* 0 0 00 */ + 0x5c, /* 0 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ @@ -2732,7 +2732,7 @@ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ + 0xfc, /* 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ @@ -2834,11 +2834,11 @@ /* 217 0xd9 '\331' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ + 0xfc, /* 00 */ 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ + 0xfc, /* 00 */ 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ + 0xfc, /* 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ diff -u --recursive --new-file v2.2.11/linux/drivers/video/offb.c linux/drivers/video/offb.c --- v2.2.11/linux/drivers/video/offb.c Thu Apr 29 12:53:48 1999 +++ linux/drivers/video/offb.c Wed Aug 25 17:29:49 1999 @@ -346,6 +346,21 @@ dp->addrs[0].size = 0x01000000; } } + + /* + * The LTPro on the Lombard powerbook has no addresses + * on the display nodes, they are on their parent. + */ + if (dp->n_addrs == 0 && device_is_compatible(dp, "ATY,264LTPro")) { + int na; + unsigned int *ap = (unsigned int *) + get_property(dp, "AAPL,address", &na); + if (ap != 0) + for (na /= sizeof(unsigned int); na > 0; --na, ++ap) + if (*ap <= addr && addr < *ap + 0x1000000) + goto foundit; + } + /* * See if the display address is in one of the address * ranges for this display. @@ -356,6 +371,7 @@ break; } if (i < dp->n_addrs) { + foundit: printk(KERN_INFO "MacOS display is %s\n", dp->full_name); macos_display = dp; break; diff -u --recursive --new-file v2.2.11/linux/drivers/video/sgivwfb.c linux/drivers/video/sgivwfb.c --- v2.2.11/linux/drivers/video/sgivwfb.c Fri Apr 16 08:20:23 1999 +++ linux/drivers/video/sgivwfb.c Wed Aug 25 17:29:49 1999 @@ -1,8 +1,8 @@ /* * linux/drivers/video/sgivwfb.c -- SGI DBE frame buffer device * - * Copyright (C) 1999 Silicon Graphics, Inc. - * Jeffrey Newquist, newquist@engr.sgi.som + * Copyright (C) 1999 Silicon Graphics, Inc. + * Jeffrey Newquist, newquist@engr.sgi.com * * 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 @@ -25,33 +25,67 @@ #include #include #include +#include #include