diff -u --recursive --new-file v2.0.35/linux/CREDITS linux/CREDITS --- v2.0.35/linux/CREDITS Sun Nov 15 10:49:20 1998 +++ linux/CREDITS Sun Nov 15 10:32:41 1998 @@ -36,10 +36,12 @@ S: USA N: Andrea Arcangeli -E: arcangeli@mbox.queen.it -W: http://www.cs.unibo.it/~arcangel/ +E: andrea@e-mind.com +W: http://e-mind.com/~andrea/ P: 1024/CB4660B9 CC A0 71 81 F4 A0 63 AC C0 4B 81 1D 8C 15 C8 E5 D: Fixed a 2.0.33 mm bug that corrupts memory in linux/mm/vmalloc.c +D: Author of lil (Linux Interrupt Latency benchmark) +D: Fixed the shm swap deallocation at swapoff time S: Via Ciaclini 26 S: Imola 40026 S: Italy @@ -383,8 +385,8 @@ W: http://www.pi.se/blox/ D: Extended support for loadable modules D: D-Link pocket adapter drivers -S: Myrstuguv. 83 -S: S-143 32 VARBY +S: Grevgatan 11 +S: S-114 53 Stockholm S: Sweden N: Fritz Elfert @@ -453,15 +455,11 @@ S: USA N: Jim Freeman -E: jfree@caldera.com +E: jfree@sovereign.org W: http://www.sovereign.org/ D: Initial GPL'd Frame Relay driver D: Dynamic PPP devices D: Sundry modularizations (PPP, IPX, ...) and fixes -S: Caldera, Inc. -S: 240 West Center St. -S: Orem, Utah 84059-1920 -S: USA N: Bob Frey E: bobf@advansys.com diff -u --recursive --new-file v2.0.35/linux/Documentation/Changes linux/Documentation/Changes --- v2.0.35/linux/Documentation/Changes Mon Jul 13 13:46:24 1998 +++ linux/Documentation/Changes Sun Nov 15 10:32:41 1998 @@ -17,10 +17,6 @@ and was originally written and maintained by Alessandro Sigala (ssigala@globalnet.it). - There is now a web page based on this material, thanks to John -Taylor. Check out http://www.cviog.uga.edu/LinuxBleed.html if you -prefer a HTML-ized shopping list. - Para aquellos que prefieran una version en castellano de este documento, consultad la traduccion de Alfredo Sanjuan en http://slug.ctv.es/~alfredo/Cambios.html (Spanish translation). diff -u --recursive --new-file v2.0.35/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.0.35/linux/Documentation/Configure.help Sun Nov 15 10:49:22 1998 +++ linux/Documentation/Configure.help Sun Nov 15 10:32:42 1998 @@ -12,7 +12,7 @@ # # Information about what a kernel is, what it does, how to patch and # compile it and much more is contained in the Kernel-HOWTO, available -# via ftp (user: anonymous) from sunsite.unc.edu in the directory +# via FTP (user: anonymous) from sunsite.unc.edu in the directory # /pub/Linux/docs/HOWTO. # # Format of this file: descriptionvariablehelptext. @@ -71,7 +71,7 @@ line option "no387", which comes handy if your coprocessor is broken. See the documentation of your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time. The lilo - procedure is also explained in the SCSI-HOWTO, available via ftp + procedure is also explained in the SCSI-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.) This means that it is a good idea to say Y here if you intend to use this kernel on different machines. More information about the internals @@ -93,15 +93,15 @@ Enabling this option will allow you to use a portion of your RAM memory as a block device, so that you can make filesystems on it, read and write to it and do all the other things that normal block - devices (such as harddrives) can do. It is usually used to load and + devices (such as hard drives) can do. It is usually used to load and store a copy of a minimal root file system off of a floppy into RAM - during the initial install of Linux. Note that the kernel command - line option "ramdisk=XX" is now obsolete. For details, read - Documentation/ramdisk.txt. 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 and read Documentation/modules.txt. Most - normal users won't need the RAM disk functionality, and can thus say - N here. + during the initial install of Linux. Note that the kernel command + line option "ramdisk=XX" is now obsolete. For details, read + Documentation/ramdisk.txt. 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 and read Documentation/modules.txt. + Most normal users won't need the RAM disk functionality, and can + thus say N here. Initial RAM disk (initrd) support CONFIG_BLK_DEV_INITRD @@ -128,16 +128,16 @@ This will use the full-featured IDE driver to control up to four IDE interfaces, for a combination of up to eight IDE disk/cdrom/tape/floppy drives. Useful information about large - (>540MB) IDE disks, soundcard IDE ports, and other topics, is all + (>540MB) IDE disks, sound card IDE ports, and other topics, is all contained in Documentation/ide.txt. If you have one or more IDE drives, say Y here. If your system has no IDE drives, or if memory requirements are really tight, you could say N here, and - select the Old harddisk driver instead to save about 13kB of + select the Old hard disk driver instead to save about 13kB of memory in the kernel. To fine-tune IDE drive/interface parameters for improved performance, look for the hdparm package at sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/ -Old harddisk (MFM/RLL/IDE) driver +Old hard disk (MFM/RLL/IDE) driver CONFIG_BLK_DEV_HD_ONLY There are two drivers for MFM/RLL/IDE disks. Most people use the newer enhanced driver, but the old one is still around for two @@ -155,7 +155,7 @@ CONFIG_BLK_DEV_HD_IDE There are two drivers for MFM/RLL/IDE disks. Most people use just the new enhanced driver by itself. This option installs the old - harddisk driver to control the primary IDE/disk interface in the + hard disk driver to control the primary IDE/disk interface in the system, leaving the new enhanced IDE driver take care of only the 2nd/3rd/4th IDE interfaces. Doing this will prevent you from having an IDE/ATAPI CDROM or tape drive connected to the primary IDE @@ -176,10 +176,10 @@ or "hdc", or something similar. If this is your only CDROM drive, you can say N to all other CDROM options, but be sure to say Y to the ISO9660 filesystem. Read the - CDROM-HOWTO, available via ftp (user: anonymous) in + CDROM-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file Documentation/cdrom/ide-cd. Note that older versions of lilo (the - linux boot loader) cannot properly deal with IDE/ATAPI CDROMs, so + Linux boot loader) cannot properly deal with IDE/ATAPI CDROMs, so install lilo-16 or higher, available from sunsite.unc.edu:/pub/Linux/system/Linux-boot/lilo. @@ -259,7 +259,7 @@ CONFIG_IDE_CHIPSETS Say Y here if you want to include enhanced support for various IDE interface chipsets used on motherboards and add-on cards. This - enhanced support may be necessary for linux to be able to access the + enhanced support may be necessary for Linux to be able to access the 3rd/4th drives in some systems. It may also enable setting of higher speed I/O rates to improve system performance with these chipsets. Most of these also require special kernel boot parameters @@ -311,7 +311,7 @@ devices are not supported yet. See the Documentation/ide.txt and promise.c files for more info. -XT harddisk support +XT hard disk support CONFIG_BLK_DEV_XD Very old 8 bit hard disk controllers used in the IBM XT computer. To include a driver for these, say Y. If you want to compile the @@ -326,19 +326,19 @@ your computer's parallel port. Most of them are actually IDE devices using a parallel port IDE adapter. This option enables the PARIDE subsystem which contains drivers for many of these external drives. - Read linux/Documentation/paride.txt for more information. If you - built PARIDE support into your kernel, you may still build the - individual protocol modules and high-level drivers as loadable - modules. If you build this support as a module, it will be called - paride.o. To use the PARIDE support, you must say Y or M here - and also to at least one high-level driver (e.g. "Parallel port - IDE disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI - disks" etc.) and to at least one protocol driver (e.g. "ATEN - EH-100 protocol", "MicroSolutions backpack protocol", "DataStor - Commuter protocol" etc.). + Read linux/Documentation/paride.txt for more information. + If you built PARIDE support into your kernel, you may still build + the individual protocol modules and high-level drivers as loadable + modules. If you build this support as a module, it will be called + paride.o. To use the PARIDE support, you must say Y or M here and + also to at least one high-level driver (e.g. "Parallel port IDE + disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI disks" + etc.) and to at least one protocol driver (e.g. "ATEN EH-100 + protocol", "MicroSolutions backpack protocol", "DataStor Commuter + protocol" etc.). Parallel port IDE disks -CONFIG_PARIDE_PD +CONFIG_PARIDE_PD This option enables the high-level driver for IDE-type disk devices connected through a parallel port. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the @@ -350,7 +350,7 @@ hard drives from MicroSolutions. Parallel port ATAPI CD-ROMs -CONFIG_PARIDE_PCD +CONFIG_PARIDE_PCD This option enables the high-level driver for ATAPI CD-ROM devices connected through a parallel port. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the @@ -364,7 +364,7 @@ on CDROMs. Parallel port ATAPI disks -CONFIG_PARIDE_PF +CONFIG_PARIDE_PF This option enables the high-level driver for ATAPI disk devices connected through a parallel port. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the @@ -390,22 +390,22 @@ CONFIG_PARIDE_PG This option enables a special high-level driver for generic ATAPI devices connected through a parallel port. The driver allows user - programs, such as cdrecord, to send ATAPI commands directly to a - device. If you chose to build PARIDE support into your kernel, you - may answer Y here to build in the parallel port generic ATAPI driver, - otherwise you should answer M to build it as a loadable module. - The module will be called pg.o. You must also have at least one - parallel port protocol driver in your system. This driver - implements an API loosely related to the generic SCSI driver. - See /usr/include/linux/pg.h for details, or visit - http://www.torque.net/parport/cdr.html for more information and - the required patches to cdrecord. + programs, such as cdrecord, to send ATAPI commands directly to + a device. If you chose to build PARIDE support into your kernel, + you may answer Y here to build in the parallel port generic ATAPI + driver, otherwise you should answer M to build it as a loadable + module. The module will be called pg.o. You must also have at + least one parallel port protocol driver in your system. This driver + implements an API loosely related to the generic SCSI driver. See + /usr/include/linux/pg.h for details. You can obtain the most recent + version of cdrecord from ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ . + Versions 1.6.1a3 and later fully support the pg driver. ATEN EH-100 protocol CONFIG_PARIDE_ATEN This option enables support for the ATEN EH-100 parallel port IDE protocol. This protocol is used in some inexpensive low performance - parallel port kits made in Hong Kong. If you chose to build PARIDE + parallel port kits made in Hong Kong. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the protocol driver, otherwise you should answer M to build it as a loadable module. The module will be called aten.o. You must also @@ -422,8 +422,8 @@ a high-level driver for the type of device that you want to support. DataStor Commuter protocol -CONFIG_PARIDE_COMM - This option enables support for the Commuter parallel port IDE +CONFIG_PARIDE_COMM + This option enables support for the Commuter parallel port IDE protocol from DataStor. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the protocol driver, otherwise you should answer M to build it as a loadable @@ -431,8 +431,8 @@ a high-level driver for the type of device that you want to support. DataStor EP-2000 protocol -CONFIG_PARIDE_DSTR - This option enables support for the EP-2000 parallel port IDE +CONFIG_PARIDE_DSTR + This option enables support for the EP-2000 parallel port IDE protocol from DataStor. If you chose to build PARIDE support into your kernel, you may answer Y here to build in the protocol driver, otherwise you should answer M to build it as a loadable @@ -440,105 +440,105 @@ a high-level driver for the type of device that you want to support. Shuttle EPAT/EPEZ protocol -CONFIG_PARIDE_EPAT - This option enables support for the EPAT parallel port IDE +CONFIG_PARIDE_EPAT + This option enables support for the EPAT parallel port IDE protocol. EPAT is a parallel port IDE adapter manufactured by Shuttle Technology and widely used in devices from major vendors - such as Hewlett-Packard, SyQuest, Imation and Avatar. If you - chose to build PARIDE support into your kernel, you may answer Y - here to build in the protocol driver, otherwise you should answer M - to build it as a loadable module. The module will be called epat.o. - You must also have a high-level driver for the type of device that + such as Hewlett-Packard, SyQuest, Imation and Avatar. If you + chose to build PARIDE support into your kernel, you may answer Y + here to build in the protocol driver, otherwise you should answer M + to build it as a loadable module. The module will be called epat.o. + You must also have a high-level driver for the type of device that you want to support. Shuttle EPIA protocol -CONFIG_PARIDE_EPIA - This option enables support for the (obsolete) EPIA parallel port - IDE protocol from Shuttle Technology. This adapter can still be found - in some no-name kits. If you chose to build PARIDE support into your - kernel, you may answer Y here to build in the protocol driver, - otherwise you should answer M to build it as a loadable module. - The module will be called epia.o. You must also have a high-level - driver for the type of device that you want to support. +CONFIG_PARIDE_EPIA + This option enables support for the (obsolete) EPIA parallel port + IDE protocol from Shuttle Technology. This adapter can still be + found in some no-name kits. If you chose to build PARIDE support + into your kernel, you may answer Y here to build in the protocol + driver, otherwise you should answer M to build it as a loadable + module. The module will be called epia.o. You must also have a + high-level driver for the type of device that you want to support. FIT TD-2000 protocol CONFIG_PARIDE_FIT2 - This option enables support for the TD-2000 parallel port IDE protocol - from Fidelity International Technology. This is a simple (low speed) - adapter that is used in some portable hard drives. If you chose to - build PARIDE support into your kernel, you may answer Y here to - build in the protocol driver, otherwise you should answer M to - build it as a loadable module. The module will be called fit2.o. - You must also have a high-level driver for the type of device - that you want to support. + This option enables support for the TD-2000 parallel port IDE + protocol from Fidelity International Technology. This is a simple + (low speed) adapter that is used in some portable hard drives. + If you chose to build PARIDE support into your kernel, you may + answer Y here to build in the protocol driver, otherwise you should + answer M to build it as a loadable module. The module will be + called fit2.o. You must also have a high-level driver for the type + of device that you want to support. FIT TD-3000 protocol CONFIG_PARIDE_FIT3 - This option enables support for the TD-3000 parallel port IDE protocol - from Fidelity International Technology. This protocol is used in newer - models of their portable disk, CD-ROM and PD/CD devices. If you chose - to build PARIDE support into your kernel, you may answer Y here to - build in the protocol driver, otherwise you should answer M to - build it as a loadable module. The module will be called fit3.o. - You must also have a high-level driver for the type of device - that you want to support. + This option enables support for the TD-3000 parallel port IDE + protocol from Fidelity International Technology. This protocol is + used in newer models of their portable disk, CD-ROM and PD/CD + devices. If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the protocol driver, otherwise you + should answer M to build it as a loadable module. The module will + be called fit3.o. You must also have a high-level driver for the + type of device that you want to support. FreeCom power protocol -CONFIG_PARIDE_FRPW - This option enables support for the Freecom power parallel port IDE - protocol. If you chose to build PARIDE support into your kernel, you - may answer Y here to build in the protocol driver, otherwise you - should answer M to build it as a loadable module. The module will be - called frpw.o. You must also have a high-level driver for the type - of device that you want to support. +CONFIG_PARIDE_FRPW + This option enables support for the Freecom power parallel port IDE + protocol. If you chose to build PARIDE support into your kernel, + you may answer Y here to build in the protocol driver, otherwise you + should answer M to build it as a loadable module. The module will + be called frpw.o. You must also have a high-level driver for the + type of device that you want to support. KingByte KBIC-951A/971A protocols -CONFIG_PARIDE_KBIC - This option enables support for the KBIC-951A and KBIC-971A parallel - port IDE protocols from KingByte Information Corp. KingByte's adapters - appear in many no-name portable disk and CD-ROM products, especially - in Europe. If you chose to build PARIDE support into your kernel, you - may answer Y here to build in the protocol driver, otherwise you should - answer M to build it as a loadable module. The module will be called - kbic.o. You must also have a high-level driver for the type of device - that you want to support. +CONFIG_PARIDE_KBIC + This option enables support for the KBIC-951A and KBIC-971A parallel + port IDE protocols from KingByte Information Corp. KingByte's + adapters appear in many no-name portable disk and CD-ROM products, + especially in Europe. If you chose to build PARIDE support into + your kernel, you may answer Y here to build in the protocol driver, + otherwise you should answer M to build it as a loadable module. + The module will be called kbic.o. You must also have a high-level + driver for the type of device that you want to support. KT PHd protocol CONFIG_PARIDE_KTTI This option enables support for the "PHd" parallel port IDE protocol from KT Technology. This is a simple (low speed) adapter that is - used in some 2.5" portable hard drives. If you chose to build PARIDE - support into your kernel, you may answer Y here to build in the - protocol driver, otherwise you should answer M to build it as a - loadable module. The module will be called ktti.o. You must also + used in some 2.5" portable hard drives. If you chose to build + PARIDE support into your kernel, you may answer Y here to build in + the protocol driver, otherwise you should answer M to build it as a + loadable module. The module will be called ktti.o. You must also have a high-level driver for the type of device that you want to support. OnSpec 90c20 protocol -CONFIG_PARIDE_ON20 - This option enables support for the (obsolete) 90c20 parallel port +CONFIG_PARIDE_ON20 + This option enables support for the (obsolete) 90c20 parallel port IDE protocol from OnSpec (often marketed under the ValuStore brand - name). If you chose to build PARIDE support into your kernel, you - may answer Y here to build in the protocol driver, otherwise you - should answer M to build it as a loadable module. The module will - be called on20.o. You must also have a high-level driver for the + name). If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the protocol driver, otherwise you + should answer M to build it as a loadable module. The module will + be called on20.o. You must also have a high-level driver for the type of device that you want to support. OnSpec 90c26 protocol -CONFIG_PARIDE_ON26 - This option enables support for the 90c26 parallel port IDE protocol +CONFIG_PARIDE_ON26 + This option enables support for the 90c26 parallel port IDE protocol from OnSpec Electronics (often marketed under the ValuStore brand - name). If you chose to build PARIDE support into your kernel, you - may answer Y here to build in the protocol driver, otherwise you - should answer M to build it as a loadable module. The module will - be called on26.o. You must also have a high-level driver for the + name). If you chose to build PARIDE support into your kernel, you + may answer Y here to build in the protocol driver, otherwise you + should answer M to build it as a loadable module. The module will + be called on26.o. You must also have a high-level driver for the type of device that you want to support. Multiple devices driver support CONFIG_BLK_DEV_MD - This driver lets you combine several harddisk partitions into one + This driver lets you combine several hard disk partitions into one logical block device. Information about how and why to use it and - the necessary tools are available over ftp (user: anonymous) from + the necessary tools are available over FTP (user: anonymous) from sweet-smoke.ufr-info-p7.ibp.fr/pub/public/Linux in the md package and the md-FAQ. Please read drivers/block/README.md. If unsure, say N. @@ -546,7 +546,7 @@ Linear (append) mode CONFIG_MD_LINEAR If you enable this, then your multiple devices driver will be able - to use the so-called linear mode, i.e. it will combine the harddisk + to use the so-called linear mode, i.e. it will combine the hard disk partitions by simply appending one to the other. 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 @@ -555,7 +555,7 @@ RAID-0 (striping) mode CONFIG_MD_STRIPED If you enable this, then your multiple devices driver will be able - to use the so-called raid0 mode, i.e. it will combine the harddisk + to use the so-called raid0 mode, i.e. it will combine the hard disk partitions into one logical device in such a fashion as to fill them up evenly, one chunk here and one chunk there. This will increase the throughput rate if the partitions reside on distinct disks. If @@ -704,19 +704,19 @@ this option turned on the TCP/IP stack will use a cryptographic challenge protocol known as SYN cookies to enable legitimate users to continue to connect, even when your machine is under attack. - If you are SYN flooded, the source address reported by the kernel is - likely to have been forged by the attacker. The source address is + If you are SYN flooded, the source address reported by the kernel is + likely to have been forged by the attacker. The source address is reported as an aid in tracing the packets to their actual source. Sun floppy controller support CONFIG_BLK_DEV_SUNFD - This is support for floppy drives on Sun Sparc workstations. Say Y + This is support for floppy drives on Sun SPARC workstations. Say Y if you have a floppy drive, otherwise N. Easy. Alpha system type CONFIG_ALPHA_AVANTI Find out what type of Alpha motherboard you have. You will probably - want to read the Linux/Alpha homepage on the WWW at + want to read the Linux/Alpha home page on the WWW at http://www.azstarnet.com/~axplinux/ (To browse the WWW, you need to have access to a machine on the Internet that has one of the programs lynx, netscape or Mosaic). For this question, it suffices @@ -760,7 +760,7 @@ to your kernel during boot time. See the documentation of your boot loader (lilo or loadlin) about how to pass options to the kernel. The lilo procedure is also explained in the SCSI-HOWTO, available - via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. + via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. You also need at least 512kB of RAM cache if you have more than 64MB of RAM. Some other things to try when experiencing seemingly random, "weird" problems: 1) passing the "no-hlt" option to the kernel @@ -815,7 +815,7 @@ http://www.undergrad.math.uwaterloo.ca/~cpbeaure/mca-linux.html on the WWW. Note2: some old PCI motherboards have BIOS bugs and may crash if "PCI bios support" is enabled (but they run fine without - this option). The PCI-HOWTO, available via ftp (user: anonymous) in + this option). The PCI-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO, contains valuable information about which PCI hardware does work under Linux and which doesn't. If some of your PCI devices don't work and you get a warning during @@ -830,7 +830,7 @@ Intel 82371 PIIX (Triton I/II) DMA support CONFIG_BLK_DEV_TRITON - If your PCI system uses an IDE harddrive (as opposed to SCSI, say) + If your PCI system uses an IDE hard drive (as opposed to SCSI, say) and includes the Intel Triton I/II IDE interface chipset (i82371FB, i82371SB or i82371AB), you will want to enable this option to allow use of bus-mastering DMA data transfers. Read the comments at the @@ -841,11 +841,11 @@ System V IPC CONFIG_SYSVIPC Inter Process Communication is a suite of library functions and - system calls which let processes (= running programs) synchronize + system calls which let processes ( = running programs) synchronize and exchange information. It is generally considered to be a good thing, and some programs won't run unless you enable this. In particular, if you want to run the DOS emulator dosemu under Linux - (read the DOSEMU-HOWTO, available via ftp (user: anonymous) in + (read the DOSEMU-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO), you'll need to say Y here. You can find documentation about IPC in ipc.info, which is contained in sunsite.unc.edu:/pub/Linux/docs/man/info.tar.gz (extract with @@ -878,7 +878,7 @@ and read Documentation/modules.txt. Saying M or N here is dangerous because some crucial programs on your system might be in ELF format. -Compile kernel as ELF - if your GCC is ELF-GCC +Compile kernel as ELF -- if your GCC is ELF-GCC CONFIG_KERNEL_ELF The gcc version 2.7.0 and newer produces the new ELF binary format as default. If you have such a compiler (try "gcc -v"), say Y here, @@ -919,10 +919,10 @@ option allows you to run a Java binary just like any other Linux program: by typing in its name. As more and more Java programs become available, the use for this will gradually increase. You can - even execute HTML files containing JAVA applets (= JAVA binaries) if - those files start with the string "". If you want to + even execute HTML files containing JAVA applets ( = JAVA binaries) + if those files start with the string "". If you want to use this, read Documentation/java.txt and the Java on Linux HOWTO, - available via ftp (user: anonymous) at + available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. You will then need to install the run time system contained in the Java Developers Kit (JDK) as described in the HOWTO. If you disable this option it will reduce @@ -935,16 +935,28 @@ Processor type CONFIG_M386 - This is the processor type of your CPU. It is used for optimizing - purposes. In order to compile a kernel that can run on all CPU types - (albeit not optimally fast), you can specify "386" here. If you - specify "486" or "Pentium" or "PPro", then the kernel will run on - 486 and Pentium (=586) and Pentium Pro (=686) CPUs. In rare cases, - it can make sense to specify "Pentium" even if running a 486: the - kernel will be smaller but slower. On the other hand, if you use a - compiler before gcc 2.7 (say "gcc -v" to find out), then you have to - say "386" or "486" here even if running on a Pentium or PPro - machine. If you don't know what to do, say "386". + This is the processor type of your CPU. It is used for optimizing + purposes. In order to compile a kernel that can run on all CPU + types (albeit not optimally fast), you can specify "386" here. If + you specify "486" or "Pentium" or "PPro", then the kernel will run + on all of these CPUs: 486 and Pentium (=586) and Pentium Pro (=686). + Here are the settings recommended for greatest speed: + - "386" for the AMD/Cyrix/Intel 386DX/DXL/SL/SLC/SX and + Cyrix/TI 486DLC/DLC2. Only "386" kernels will run on a 386 class + machine. + - "486" for the AMD/Cyrix/IBM/Intel DX4 or 486DX/DX2/SL/SX/SX2, + AMD/Cyrix 5x86, NexGen Nx586 and UMC U5D or U5S + - "Pentium" for the AMD K5, K6 and K6-3D, Cyrix MediaGX, + Cyrix/IBM/National Semiconductor 6x86 and GXm, IDT Centaur + WinChip C6, and Intel Pentium/Pentium MMX + - "PPro" for the Cyrix/IBM/National Semiconductor 6x86MX, MII and + Intel Pentium II/Pentium Pro + In rare cases, it can make sense to specify "Pentium" even if + running a 486: the kernel will be smaller but slightly slower. On + the other hand, if you use a compiler before gcc 2.7 (say "gcc -v" + to find out), then you have to say "386" or "486" here even if + running on a Pentium or PPro machine. + If you don't know what to do, say "386". Compile the kernel into the ELF object format CONFIG_ELF_KERNEL @@ -956,8 +968,8 @@ Is your ELF compiler an extra compiler CONFIG_EXTRA_ELF_COMPILER - If you have a linuxelf-gcc as opposed to linux-gcc, say Y, otherwise - N. + If you have a linuxelf-gcc as opposed to linux-gcc, say Y, + otherwise N. Generate little endian code CONFIG_CPU_LITTLE_ENDIAN @@ -1004,7 +1016,7 @@ and screen blanker programs later on. The "kerneld" daemon is included in the package "modules-1.2.8" and later. You will probably want to read the kerneld mini-HOWTO, - available via ftp (user: anonymous) from + available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If unsure, say Y. ARP daemon support (EXPERIMENTAL) @@ -1036,7 +1048,7 @@ necessary if you want to use the full power of term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix - computer. Read the Term-HOWTO, available via ftp (user: anonymous) + computer. Read the Term-HOWTO, available via FTP (user: anonymous) on sunsite.unc.edu:/pub/Linux/docs/HOWTO). Short answer: say Y. IP: forwarding/gatewaying @@ -1045,15 +1057,15 @@ network (i.e. the computer responsible for distributing Internet traffic to and from the machines in the local network and the subnetworks) should say Y here (thereby enlarging their kernel by - about 5 kB). Note that in this case, you possibly have two ethernet + about 5 kB). Note that in this case, you possibly have two Ethernet devices in your computer: one for the "outside world" and one for your local net. The kernel is not able to recognize both at boot time without help; for details read the - Multiple-Ethernet-mini-HOWTO, available via ftp (user: anonymous) in + Multiple-Ethernet-mini-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If your box is connected to two networks, it may still make sense to say N here, namely if you want to turn your box into a firewall protecting a - local network from the internet. The Firewall-HOWTO tells you how to + local network from the Internet. The Firewall-HOWTO tells you how to do this. If your setup is more complex, say you are connected to three networks and you want to act as a firewall between two of them and route traffic for the others, you need to say Y here and enable @@ -1069,7 +1081,7 @@ (Address Resolution Protocol), explained in the Proxy-Arp mini howto on sunsite in /pub/Linux/docs/HOWTO/mini. You also need to say Y here if you want to run mrouted in order to do multicast routing as - used on the MBONE (a high bandwidth network on top of the internet + used on the MBONE (a high bandwidth network on top of the Internet which carries audio and video broadcasts) for example. In this case, say Y to "IP: multicasting" and "IP: multicast routing" as well. If unsure, say N. @@ -1081,7 +1093,7 @@ daemon that updates your computer's routing tables, you will need to have this option compiled in. You also need multicasting if you intend to participate in the MBONE, a high bandwidth network on top - of the internet which carries audio and video broadcasts. More + of the Internet which carries audio and video broadcasts. More information about the MBONE is on the WWW at http://www.best.com/~prince/techinfo/mbone.html (to browse the WWW, you need to have access to a machine on the Internet that has one of @@ -1102,7 +1114,7 @@ If you want to configure your Linux box as a firewall for a local TCP/IP based network, say Y here. This will enlarge your kernel by about 2kB. You may need to read the FIREWALL-HOWTO, available via - ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. + FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you will need the ipfwadm tool (check the file Documentation/Changes for location and latest version) to allow selective blocking of internet traffic based on type, origin and @@ -1174,7 +1186,7 @@ corresponding local computer. This way, the computers on your local net are completely invisible to the outside world, even though they can reach the outside and can be reached. This makes it possible to - have the computers on the local network participate on the internet + have the computers on the local network participate on the Internet even if they don't have officially registered IP addresses. (This last problem can also be solved by connecting the Linux box to the Internet using SLiRP [SLiRP is a SLIP/PPP emulator that works if you @@ -1205,12 +1217,12 @@ IP: always defragment CONFIG_IP_ALWAYS_DEFRAG - This option means that all incoming fragments (= parts of IP packets - that arose when some host between origin and destination decided - that the IP packets were too large and cut them in pieces) will be - reassembled (defragmented) before being processed, even if they are - about to be forwarded. This option is highly recommended if you - have enabled the masquerading support (CONFIG_IP_MASQUERADE), + This option means that all incoming fragments ( = parts of IP + packets that arose when some host between origin and destination + decided that the IP packets were too large and cut them in pieces) + will be reassembled (defragmented) before being processed, even if + they are about to be forwarded. This option is highly recommended + if you have enabled the masquerading support (CONFIG_IP_MASQUERADE), because this facility requires that second and further fragments can be related to TCP or UDP port numbers, which are only stored in the first fragment. When using IP firewall support (CONFIG_IP_FIREWALL), @@ -1226,15 +1238,15 @@ IP: aliasing support CONFIG_IP_ALIAS Sometimes it is useful to give several addresses to a single network - interface (= serial port or ethernet card). The most common case is - that you want to serve different WWW documents to the outside - according to which of your host names they used to connect to - you. This is explained in detail on the WWW at + interface ( = serial port or Ethernet card). The most common case + is that you want to serve different WWW documents to the outside + according to which of your host names they used to connect to you. + This is explained in detail on the WWW at http://www.thesphere.com/~dlp/TwoServers/ (to browse the WWW, you need to have access to a machine on the Internet that has one of the programs lynx, netscape or Mosaic). Another scenario would be that - there are two logical networks living on your local ethernet and you - want to access them both with the same ethernet card. The + there are two logical networks living on your local Ethernet and you + want to access them both with the same Ethernet card. The configuration of these alias addresses is done with a special name syntax explained in Documentation/networking/alias.txt. If you want this, say Y. Most people don't need it and say N. @@ -1243,7 +1255,7 @@ CONFIG_IP_MROUTE This is used if you want your machine to act as a router for IP packets that have several destination addresses. It is needed on the - MBONE, a high bandwidth network on top of the internet which carries + MBONE, a high bandwidth network on top of the Internet which carries audio and video broadcasts. In order to do that, you would most likely run the program mrouted. Information about the multicast capabilities of the various network cards is contained in @@ -1252,17 +1264,17 @@ PC/TCP compatibility mode CONFIG_INET_PCTCP - If you have been having difficulties telnetting to your Linux machine - from a DOS system that uses (broken) PC/TCP networking software (all - versions up to OnNet 2.0) over your local ethernet try enabling this - option. Everyone else says N. + If you have been having difficulties telnetting to your Linux + machine from a DOS system that uses (broken) PC/TCP networking + software (all versions up to OnNet 2.0) over your local Ethernet try + enabling this option. Everyone else says N. People having problems with NCSA telnet should see the file linux/Documentation/networking/ncsa-telnet. Reverse ARP CONFIG_INET_RARP Since you asked: if there are (usually diskless or portable) - machines on your local network that know their hardware ethernet + machines on your local network that know their hardware Ethernet addresses but don't know their IP addresses upon startup, they can send out a Reverse Address Resolution Protocol (RARP) request to find out their own IP addresses. Diskless Sun 3 machines use this @@ -1304,7 +1316,7 @@ Disable NAGLE algorithm (normally enabled) CONFIG_TCP_NAGLE_OFF The NAGLE algorithm works by requiring an acknowledgment before - sending small IP frames (= packets). This keeps tiny telnet and + sending small IP frames ( = packets). This keeps tiny telnet and rlogin packets from congesting Wide Area Networks. Most people strongly recommend to say N here, thereby leaving NAGLE enabled. Those programs that would benefit from disabling this facility can @@ -1312,19 +1324,19 @@ IP: Drop source routed frames CONFIG_IP_NOSR - Usually, the originator of an IP frame (= packet) specifies only the - destination, and the hosts along the way do the routing, i.e. they - decide how to forward the frame. However, there is a feature of the - IP protocol that allows to specify the full route for a given frame - already at its origin. A frame with such a fully specified route is - called "source routed". The question now is whether we should - honour these route requests when such frames arrive, or if we should - drop all those frames instead. Honouring them can introduce - security problems (and is rarely a useful feature), and hence it is - recommended that you say Y here unless you really know what you're - doing. + Usually, the originator of an IP frame ( = packet) specifies only + the destination, and the hosts along the way do the routing, i.e. + they decide how to forward the frame. However, there is a feature + of the IP protocol that allows to specify the full route for a given + frame already at its origin. A frame with such a fully specified + route is called "source routed". The question now is whether we + should honour these route requests when such frames arrive, or if + we should drop all those frames instead. Honouring them can + introduce security problems (and is rarely a useful feature), and + hence it is recommended that you say Y here unless you really know + what you're doing. -IP: Allow large windows (not recommend if <16Mb of memory) +IP: Allow large windows (not recommend if <16MB of memory) CONFIG_SKB_LARGE On high speed, long distance networks the performance limit on networking becomes the amount of data a machine can buffer until the @@ -1332,7 +1344,7 @@ of bits between New York and London...) This option allows larger amounts of data to be "in flight" at a given time. It also means a user process can require a lot more memory for network buffers and - thus this option is best only used on machines with 16Mb of + thus this option is best only used on machines with 16MB of memory or higher. Unless you are using long links with end to end speeds of over 2Mbit a second or satellite links this option will make no difference to @@ -1342,13 +1354,13 @@ CONFIG_IPX This is support for the Novell networking protocol, IPX, commonly used for local networks of Windows machines. You need it if you want - to access Novell Netware file or print servers using the Linux - Novell client ncpfs (available via ftp (user: anonymous) from + to access Novell NetWare file or print servers using the Linux + Novell client ncpfs (available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/system/Filesystems/) or from within the Linux DOS emulator dosemu (read the DOSEMU-HOWTO, available in sunsite.unc.edu:/pub/Linux/docs/HOWTO). In order to do the former, you'll also have to say Y to "NCP filesystem support", below. To - turn your Linux box into a fully featured Netware file server and + turn your Linux box into a fully featured NetWare file server and IPX router, say Y here and fetch either lwared from sunsite.unc.edu:/pub/Linux/system/Network/daemons/ or mars_nwe from ftp.gwdg.de:/pub/linux/misc/ncpfs. For more information, read the @@ -1376,35 +1388,35 @@ internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs. If you don't know what you are doing, say N. -Appletalk DDP +AppleTalk DDP CONFIG_ATALK - Appletalk is the way Apple computers speak to each other on a - network. EtherTalk is the name used for appletalk over ethernet - and Localtalk is appletalk over the apple serial links. If your - linux box is connected to such a network and you want to join the + AppleTalk is the way Apple computers speak to each other on a + network. EtherTalk is the name used for AppleTalk over Ethernet + and LocalTalk is AppleTalk over the Apple serial links. If your + Linux box is connected to such a network and you want to join the conversation, say Y. You will need to use the netatalk package so that your Linux box can act as a print and file server for - macs as well as access appletalk printers. Check out + macs as well as access AppleTalk printers. Check out http://artoo.hitchcock.org/~flowerpt/projects/linux-netatalk/ on the WWW for details (to browse the WWW, you need to have access to a machine on the Internet that has one of the programs lynx, - netscape or Mosaic). The NET-2-HOWTO, available via ftp (user: + netscape or Mosaic). The NET-2-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO contains valuable information as well. This driver is also available as a - module (= code which can be inserted in and removed from the running - kernel whenever you want). If you want to compile it as a module, - say M here and read Documentation/modules.txt. I hear that the GNU - boycott of Apple is over, so even politically correct people are - allowed to say Y here. At the time the kernel is released the + module ( = code which can be inserted in and removed from the + running kernel whenever you want). If you want to compile it as a + module, say M here and read Documentation/modules.txt. I hear that + the GNU boycott of Apple is over, so even politically correct people + are allowed to say Y here. At the time the kernel is released the localtalk drivers are not yet ready to ship. The kernel however supports localtalk and when such drivers become available all you - will need to do is download and install the localtalk driver. + will need to do is download and install the localtalk driver. Amateur Radio AX.25 Level 2 CONFIG_AX25 This is the protocol used for computer communication over amateur radio. It is either used by itself for point-to-point links, or to - carry other protocols such as tcp/ip. To use it, you need a device + carry other protocols such as TCP/IP. To use it, you need a device that connects your Linux box to your amateur radio. You can either use a low speed TNC (a Terminal Node Controller acts as a kind of modem connecting your computer's serial port to your radio's @@ -1415,7 +1427,7 @@ own driver) and the other baycom cards (SCC) (supported by the Z8530 driver). Information about where to get supporting software for Linux amateur radio as well as information about how to configure an - AX.25 port is contained in the HAM-HOWTO, available via ftp (user: + AX.25 port is contained in the HAM-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. You might also want to check out the file Documentation/networking/ax25.txt in the kernel source. More information about digital amateur radio in @@ -1429,7 +1441,7 @@ NET/ROM is a network layer protocol on top of AX.25 useful for routing. A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an - AX.25 port is contained in the HAM-HOWTO, available via ftp (user: + AX.25 port is contained in the HAM-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. You also might also want to check out the file Documentation/networking/ax25.txt. More information about digital amateur radio in general is on the @@ -1442,24 +1454,24 @@ CONFIG_BPQETHER AX.25 is the protocol used for computer communication over amateur radio. If you say Y here, you will be able to send and receive AX.25 - traffic over ethernet (also called "BPQ AX.25"), which could be + traffic over Ethernet (also called "BPQ AX.25"), which could be useful if some other computer on your local network has a direct amateur radio connection. Bridging (EXPERIMENTAL) CONFIG_BRIDGE If you say Y here, then your Linux box will be able to act as an - ethernet bridge, which means that the different ethernet segments it - is connected to will appear as one ethernet to the participants. + Ethernet bridge, which means that the different Ethernet segments it + is connected to will appear as one Ethernet to the participants. Several such bridges can work together to create even larger - networks of ethernets using the IEEE802.1 spanning tree algorithm. + networks of Ethernets using the IEEE802.1 spanning tree algorithm. As this is a standard, Linux bridges will interwork properly with other third party bridge products. In order to use this, you'll need - the bridge configuration tools available via ftp (user: anonymous) + the bridge configuration tools available via FTP (user: anonymous) from shadow.cabi.net. Note that if your box acts as a bridge, it - probably contains several ethernet devices, but the kernel is not + probably contains several Ethernet devices, but the kernel is not able to recognize more than one at boot time without help; for - details read the Multiple-Ethernet-mini-HOWTO, available via ftp + details read the Multiple-Ethernet-mini-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. The Bridging code is still in test. If unsure, say N. @@ -1486,24 +1498,25 @@ SCSI support? CONFIG_SCSI - If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or + If you want to use a SCSI hard disk, SCSI tapedrive, SCSI CDROM or any other SCSI device under Linux, say Y and make sure that you know the name of your SCSI host adapter (the card inside your computer that "speaks" the SCSI protocol), because you will be asked for it. You also need to say Y here if you want support for the parallel port version of the 100MB IOMEGA ZIP drive. Please read the - SCSI-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu: + SCSI-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu: /pub/Linux/docs/HOWTO. This driver is also available as a module - (= code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt and Documentation/scsi.txt. + ( = code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read Documentation/modules.txt and + Documentation/scsi.txt. SCSI disk support CONFIG_BLK_DEV_SD - If you want to use a SCSI harddisk or the SCSI or parallel port - version of the IOMEGA ZIP drive under Linux, say Y and read - the SCSI-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This is NOT for SCSI CDROMs. + If you want to use a SCSI hard disk or the SCSI or parallel port + version of the IOMEGA ZIP drive under Linux, say Y and read the + SCSI-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu: + /pub/Linux/docs/HOWTO. This is NOT for SCSI CDROMs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read @@ -1512,13 +1525,13 @@ SCSI tape support CONFIG_CHR_DEV_ST If you want to use a SCSI tapedrive under Linux, say Y and read the - SCSI-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO and drivers/scsi/README.st in - the kernel source. This is NOT for SCSI CDROMs. This driver is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile - it as a module, say M here and read Documentation/modules.txt and - Documentation/scsi.txt . + SCSI-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu: + /pub/Linux/docs/HOWTO and drivers/scsi/README.st in the kernel + source. This is NOT for SCSI CDROMs. This driver is also available + as a module ( = code which can be inserted in and removed from the + running kernel whenever you want). If you want to compile it as a + module, say M here and read Documentation/modules.txt and + Documentation/scsi.txt. SCSI CDROM support CONFIG_BLK_DEV_SR @@ -1533,15 +1546,15 @@ SCSI generic support CONFIG_CHR_DEV_SG If you want to use SCSI scanners, synthesizers or CD-writers or just - about anything having "SCSI" in its name other than harddisks, + about anything having "SCSI" in its name other than hard disks, CDROMs or tapes, say Y here. Those won't be supported by the kernel directly, so you need some additional software which knows how to talk to these devices using the SCSI protocol. For CD-writers, you - would need the program cdwrite, available via ftp (user: anonymous) + would need the program cdwrite, available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/utils/disk-management; for other devices, it's possible that you'll have to write the driver software yourself, so have a look at the SCSI-HOWTO and at the - SCSI-Programming-HOWTO, both available via ftp (user: anonymous) in + SCSI-Programming-HOWTO, both available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -1567,27 +1580,26 @@ CONFIG_SCSI_ADVANSYS This is a driver for all SCSI host adapters manufactured by AdvanSys. It is documented in the kernel source in - drivers/scsi/advansys.c. This driver is also available as a module ( - = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + drivers/scsi/advansys.c. This driver is also available as a module + ( = code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read Documentation/modules.txt. Adaptec AHA152X/2825 support CONFIG_SCSI_AHA152X This is support for the AHA-1510, AHA-1520, AHA-1522, and AHA-2825 SCSI host adapters. It is explained in section 3.3 of the - SCSI-HOWTO, available via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. You might also want to read - the comments at the top of drivers/scsi/aha152x.c. This driver is - also available as a module ( = code which can be inserted in and - removed from the running kernel whenever you want). If you want to - compile it as a module, say M here and read - Documentation/modules.txt. + SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu: + /pub/Linux/docs/HOWTO. You might also want to read the comments at + the top of drivers/scsi/aha152x.c. This driver is also available as + a module ( = code which can be inserted in and removed from the + running kernel whenever you want). If you want to compile it as a + module, say M here and read Documentation/modules.txt. Adaptec AHA1542 support CONFIG_SCSI_AHA1542 This is support for a SCSI host adapter. It is explained in section - 3.4 of the SCSI-HOWTO, available via ftp (user: anonymous) at + 3.4 of the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that Trantor was recently purchased by Adaptec, and some former Trantor products are being sold under the Adaptec name. If it doesn't work out of the @@ -1599,7 +1611,7 @@ Adaptec AHA1740 support CONFIG_SCSI_AHA1740 This is support for a SCSI host adapter. It is explained in - section 3.5 of the SCSI-HOWTO, available via ftp (user: anonymous) + section 3.5 of the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may have to change some settings in drivers/scsi/aha1740.h. This driver is also available as a module @@ -1667,14 +1679,14 @@ more time after a bus reset to be ready for the next command, but most hard drives and CD-ROM devices are ready in only a few seconds. This option has a maximum upper limit of 20 seconds to avoid bad - interactions between the aic7xxx driver and the rest of the linux + interactions between the aic7xxx driver and the rest of the Linux kernel. The default value has been reduced. If this doesn't work with your hardware, try increasing this value. Default: 5 BusLogic SCSI support CONFIG_SCSI_BUSLOGIC This is support for BusLogic MultiMaster and FlashPoint SCSI Host - Adapters. Consult the SCSI-HOWTO, available via anonymous ftp from + Adapters. Consult the SCSI-HOWTO, available via anonymous FTP from sunsite.unc.edu:/pub/Linux/docs/HOWTO, and the files README.BusLogic and README.FlashPoint in drivers/scsi for more information. If this driver does not work correctly without modification, please contact @@ -1694,21 +1706,21 @@ DTC3180/3280 SCSI support CONFIG_SCSI_DTC3280 This is support for DTC 3180/3280 SCSI Host Adapters. Please read - the SCSI-HOWTO, available via ftp (user: anonymous) at + the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file drivers/scsi/README.dtc3x80. This driver is also available as a - module (= code which can be inserted in and removed from the running - kernel whenever you want). If you want to compile it as a module, - say M here and read Documentation/modules.txt. + module ( = code which can be inserted in and removed from the + running kernel whenever you want). If you want to compile it as a + module, say M here and read Documentation/modules.txt. EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support CONFIG_SCSI_EATA_DMA This is support for the EATA-DMA protocol compliant SCSI Host Adapters like the SmartCache III/IV, SmartRAID controller families and the DPT PM2011B and PM2012B controllers. Please read the - SCSI-HOWTO, available via ftp (user: anonymous) at + SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also - available as a module (= code which can be inserted in and removed + available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. @@ -1717,9 +1729,9 @@ This driver supports all EATA-PIO protocol compliant SCSI Host Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant host adapters could also use this driver but are discouraged from - doing so, since this driver only supports harddisks and lacks + doing so, since this driver only supports hard disks and lacks numerous features. You might want to have a look at the SCSI-HOWTO, - available via ftp (user: anonymous) at + available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -1731,7 +1743,7 @@ The source at drivers/scsi/u14-34f.c contains some information about this hardware. If the driver doesn't work out of the box, you may have to change some settings in drivers/scsi/u14-34f.c. - Read the SCSI-HOWTO, available via ftp (user: anonymous) at + Read the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that there is also another driver for the same hardware: "UltraStor SCSI support", below. You should enable both only if you want 24F support as well. @@ -1746,7 +1758,7 @@ (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and other adapters based on the Future Domain chipsets (Quantum ISA-200S, ISA-250MG; Adaptec AHA-2920; and at least one IBM board). - It is explained in section 3.7 of the SCSI-HOWTO, available via ftp + It is explained in section 3.7 of the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -1757,7 +1769,7 @@ CONFIG_SCSI_GENERIC_NCR5380 This is the generic NCR family of SCSI controllers, not to be confused with the NCR 53c7 or 8xx controllers. It is explained in - section 3.8 of the SCSI-HOWTO, available via ftp (user: anonymous) + section 3.8 of the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may have to change some settings in drivers/scsi/g_NCR5380.h. This driver is also available as a module @@ -1767,7 +1779,7 @@ Enable NCR53c400 extensions CONFIG_SCSI_GENERIC_NCR53C400 - This enables certain optimizations for the NCR53c400 scsi cards. You + This enables certain optimizations for the NCR53c400 SCSI cards. You might as well try it out. Note that this driver will only probe for the Trantor T130B in its default configuration; you might have to pass a command line option to the kernel at boot time if it doesn't @@ -1785,13 +1797,13 @@ CONFIG_SCSI_NCR53C7xx This is the 53c7 and 8xx NCR family of SCSI controllers, not to be confused with the NCR 5380 controllers. It is explained in section - 3.8 of the SCSI-HOWTO, available via ftp (user: anonymous) at + 3.8 of the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may have to change some settings in - drivers/scsi/53c7,8xx.h. This driver is also available as a module ( - = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + drivers/scsi/53c7,8xx.h. This driver is also available as a module + ( = code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read Documentation/modules.txt. Always negotiate synchronous transfers CONFIG_SCSI_NCR53C7xx_sync @@ -1819,10 +1831,10 @@ NCR53C8XX SCSI support CONFIG_SCSI_NCR53C8XX - This is the BSD ncr driver adapted to linux for the NCR53C8XX family + This is the BSD ncr driver adapted to Linux for the NCR53C8XX family of PCI-SCSI controllers. This driver supports parity checking, - tagged command queuing, fast scsi II transfer up to 10 MB/s with - narrow scsi devices and 20 MB/s with wide scsi devices. + tagged command queuing, fast SCSI II transfer up to 10 MB/s with + narrow SCSI devices and 20 MB/s with wide SCSI devices. Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875 controllers has been recently added to the driver. Please read drivers/scsi/README.ncr53c8xx for more information. @@ -1830,11 +1842,11 @@ Synchronous data transfers frequency CONFIG_SCSI_NCR53C8XX_SYNC - SCSI-2 specifications allow scsi devices to negotiate a synchronous + SCSI-2 specifications allow SCSI devices to negotiate a synchronous transfer period of 25 nano-seconds or more. The transfer period value is 4 times the agreed transfer period. So, data can be transferred at a 10 MHz frequency, allowing 10 - MB/second throughput with 8 bits scsi-2 devices and 20 MB/second + MB/second throughput with 8 bits SCSI-2 devices and 20 MB/second with wide16 devices. This frequency can be used safely with differential devices but may cause problems with singled-ended devices. @@ -1842,7 +1854,7 @@ Otherwise, specify a value between 5 and 10. Commercial O/Ses generally use 5 Mhz frequency for synchronous transfers. It is a reasonable default value. - However, a flawless singled-ended scsi bus supports 10 MHz data + However, a flawless singled-ended SCSI bus supports 10 MHz data transfers. Regardless the value chosen in the Linux configuration, the synchronous period can be changed after boot-up through the /proc/scsi file system. The generic command is: @@ -1861,16 +1873,16 @@ Not allow targets to disconnect CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT - This option is only provided for safety if you suspect some scsi + This option is only provided for safety if you suspect some SCSI device of yours to not support properly the target-disconnect feature. In that case, you would say Y here. In general however, to not allow targets to disconnect is not reasonable if there is more - than 1 device on a scsi bus. The normal answer therefore is N. + than 1 device on a SCSI bus. The normal answer therefore is N. Enable tagged command queuing CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE This option allows you to enable tagged command queuing support at - linux start-up. Some scsi devices do not properly support this + Linux start-up. Some SCSI devices do not properly support this feature. The suggested method is to say N here and to use the "settags" control command after boot-up to enable this feature: echo "settags 2 4" >/proc/scsi/ncr53c8xx/0 @@ -1923,7 +1935,7 @@ The driver behaves correctly on my system with this option enabled. (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev 0x12). This option must be set to N if your system has at least one - 53C8XX based scsi board with a vendor-specific BIOS (example: Tekram + 53C8XX based SCSI board with a vendor-specific BIOS (example: Tekram DC-390/U/W/F). If unsure, say N. However, if all your non Symbios compatible boards have NVRAM, setting option CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT allows the driver @@ -1942,7 +1954,7 @@ PAS16 SCSI support CONFIG_SCSI_PAS16 This is support for a SCSI host adapter. It is explained in section - 3.10 of the SCSI-HOWTO, available via ftp (user: anonymous) at + 3.10 of the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may have to change some settings in drivers/scsi/pas16.h. @@ -1954,7 +1966,7 @@ does NOT support the PCI version. The PCI versions are supported by the Qlogic ISP driver though. Information about this driver is contained in drivers/scsi/README.qlogicfas. You should also read - the SCSI-HOWTO, available via ftp (user: anonymous) at + the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile @@ -1967,7 +1979,7 @@ card is supported by the "AM53/79C974 PCI SCSI" driver.) If you say Y here, make sure to say Y to "PCI BIOS support" as well. More information is contained in the file drivers/scsi/README.qlogicisp. - You should also read the SCSI-HOWTO, available via ftp (user: + You should also read the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you @@ -1978,7 +1990,7 @@ CONFIG_SCSI_SEAGATE These are 8-bit SCSI controllers; the ST-01 is also supported by this driver. It is explained in section 3.9 of the SCSI-HOWTO, - available via ftp (user: anonymous) at sunsite.unc.edu: + available via FTP (user: anonymous) at sunsite.unc.edu: /pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may have to change some settings in drivers/scsi/seagate.h. This driver is also available as a module ( = code which can be @@ -1989,7 +2001,7 @@ Trantor T128/T128F/T228 SCSI support CONFIG_SCSI_T128 This is support for a SCSI host adapter. It is explained in section - 3.11 of the SCSI-HOWTO, available via ftp (user: anonymous) at + 3.11 of the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may have to change some settings in drivers/scsi/t128.h. Note that Trantor was recently purchased by Adaptec, and some former @@ -1999,12 +2011,12 @@ CONFIG_SCSI_ULTRASTOR This is support for the UltraStor 14F, 24F and 34F SCSI-2 host adapter family. This driver is explained in section 3.12 of the - SCSI-HOWTO, available via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of - the box, you may have to change some settings in - drivers/scsi/ultrastor.h. 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. + SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu: + /pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may + have to change some settings in drivers/scsi/ultrastor.h. 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. Note that there is also another driver for UltraStor hardware: "UltraStor 14F/34F support", above. @@ -2027,7 +2039,7 @@ Note that there is also another driver for the same hardware: "EATA-DMA support". You should enable only one of them. You want to read the start of drivers/scsi/eata.c and the - SCSI-HOWTO, available via ftp (user: anonymous) at + SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -2037,7 +2049,7 @@ CONFIG_SCSI_NCR53C406A This is support for the NCR53c406a SCSI host adapter. For user configurable parameters, check out drivers/scsi/NCR53c406.c in the - kernel source. Also read the SCSI-HOWTO, available via ftp (user: + kernel source. Also read the SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here @@ -2054,7 +2066,7 @@ CONFIG_SCSI_AM53C974 This is support for the AM53/79C974 SCSI host adapters. Please read drivers/scsi/README.AM53C974 for details. Also, the SCSI-HOWTO, - available via ftp (user: anonymous) at + available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO, is for you. Use the native DC390 driver if you've got a Tekram DC390(T) PCI-SCSI host adapter. @@ -2066,7 +2078,7 @@ drivers/scsi/gdth.c and drivers/scsi/gdth.h. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile - it as a module, say M here and read Documentation/modules.txt. + it as a module, say M here and read Documentation/modules.txt. IOMEGA Parallel Port ZIP drive SCSI support CONFIG_SCSI_PPA @@ -2074,7 +2086,7 @@ (a 100Mb removable media device). For more information about this driver and how to use it you should read the file drivers/scsi/README.ppa. You should also read the SCSI-HOWTO, which - is available via anonymous ftp from sunsite.unc.edu in the directory + is available via anonymous FTP from sunsite.unc.edu in the directory /pub/Linux/docs/HOWTO. This driver is also available as a module which can be inserted in and removed from the running kernel whenever you want. If you want to use any two of a parallel port ZIP @@ -2085,7 +2097,7 @@ the SCSI version of the ZIP drive: it will be supported automatically if you enabled the generic "SCSI disk support", above. -IOMEGA ZIP drive - Buggy EPP chipset support +IOMEGA ZIP drive -- Buggy EPP chipset support CONFIG_SCSI_PPA_HAVE_PEDANTIC Contacts with the Iomega driver development team indicate there are a few reputably bad EPP implementations in existance. The following @@ -2099,16 +2111,16 @@ You can say N here in case you don't intend to connect to any other computer at all or all your connections will be either via UUCP (UUCP is a protocol to forward mail and news between unix hosts over - telephone lines; read the UUCP-HOWTO, available via ftp (user: + telephone lines; read the UUCP-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO) or dialing up a shell account or a BBS, even using term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer. Read the Term-HOWTO). You'll have to say Y if your computer contains a - network card that you want to use under linux (make sure you know + network card that you want to use under Linux (make sure you know its name because you will be asked for it and read the Ethernet-HOWTO; also, if you plan to use more than one network card - under linux, read the Multiple-Ethernet-mini-HOWTO, available from + under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you want to use SLIP (Serial Line Internet Protocol is the protocol used to send Internet traffic over telephone lines or nullmodem cables) or CSLIP @@ -2124,10 +2136,10 @@ CONFIG_NET_ETHERNET Ethernet is the most common protocol used on Local Area Networks (LANs) in universities or companies. 10-base-2 and 10-base-T and - 100-base- are common types of ethernet. If your Linux - machine will be connected to an Ethernet and you have an ethernet + 100-base- are common types of Ethernet. If your Linux + machine will be connected to an Ethernet and you have an Ethernet network card installed in your computer, say Y here and read the - Ethernet-HOWTO, available via ftp (user: anonymous) from + Ethernet-HOWTO, available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this question won't directly affect the kernel: saying N will just cause this configure script to skip all the questions about Ethernet @@ -2140,7 +2152,7 @@ address. It is most commonly used in order to make your currently inactive SLIP address seem like a real address for local programs. If you use SLIP or PPP, you might want to enable it. Read about it - in the Network Administrator's Guide, available via ftp (user: + in the Network Administrator's Guide, available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/LDP. Since this thing comes often handy, the default is Y. It won't enlarge your kernel either. What a deal. If you want to compile this as a @@ -2160,10 +2172,10 @@ over telephone lines or serial cables (also known as nullmodems). Normally, your access provider has to support SLIP in order for you to be able to use it, but there is now a SLIP emulator called SLiRP - around (available via ftp (user: anonymous) from sunsite.unc.edu: + around (available via FTP (user: anonymous) from sunsite.unc.edu: /pub/Linux/system/Network/serial/) which allows you to use SLIP over a regular dial up shell connection. If you plan to use SLiRP, make - sure to say Y to CSLIP, below. The NET-2-HOWTO, available via ftp + sure to say Y to CSLIP, below. The NET-2-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO, explains how to configure SLIP. Note that you don't need this option if you just want to run term (term is a program which gives you almost full @@ -2179,13 +2191,13 @@ CONFIG_SLIP_COMPRESSED This protocol is faster than SLIP because it uses compression on the TCP/IP headers (not on the data itself), but it has to be supported - on both ends. Ask your access provider if you are not sure and say - Y, just in case. You will still be able to use plain SLIP. If you - plan to use SLiRP, the SLIP emulator (available via ftp (user: + on both ends. Ask your access provider if you are not sure and + say Y, just in case. You will still be able to use plain SLIP. If + you plan to use SLiRP, the SLIP emulator (available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/) which allows you to use SLIP over a regular dial up shell - connection, you definitely want to say Y here. The NET-2-HOWTO, - available via ftp (user: anonymous) in + connection, you definitely want to say Y here. + The NET-2-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO, explains how to configure CSLIP. This won't enlarge your kernel. @@ -2208,12 +2220,12 @@ Radio network interfaces CONFIG_NET_RADIO Radio based interfaces for Linux. This includes amateur radio - (AX.25), support for wireless ethernet and other systems. Note that + (AX.25), support for wireless Ethernet and other systems. Note that the answer to this question won't directly affect the kernel: saying N will just cause this configure script to skip all the questions about radio interfaces. Some user-level drivers for scarab devices which don't require special kernel support are available via - ftp (user: anonymous) from shadow.cabi.net. If unsure, say N. + FTP (user: anonymous) from shadow.cabi.net. If unsure, say N. PPP (point-to-point) support CONFIG_PPP @@ -2222,7 +2234,7 @@ serial) lines. Ask your access provider if they support it, because otherwise you can't use it (not quite true any more: the free program SLiRP can emulate a PPP line if you just have a regular dial - up shell account on some UNIX computer; get it via ftp (user: + up shell account on some UNIX computer; get it via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/). To use PPP, you need an additional program called pppd as described in Documentation/networking/ppp.txt and in the PPP-HOWTO, available @@ -2248,7 +2260,7 @@ CONFIG_PPP_LOTS Saying Y here will allow you to have up to 16 PPP connections running in parallel. This is mainly useful if you intend your - linux box to act as a dial-in PPP server. Most people can say N. + Linux box to act as a dial-in PPP server. Most people can say N. STRIP (Starmode Radio IP) support CONFIG_STRIP @@ -2278,7 +2290,7 @@ These cards are used to connect your Linux box to an amateur radio in order to communicate with other computers. If you want to use this, read Documentation/networking/z8530drv.txt and the HAM-HOWTO, - available via ftp (user: anonymous) at + available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -2289,9 +2301,9 @@ This is an experimental driver for Baycom style simple amateur radio modems that connect to either a serial interface or a parallel interface. The driver supports the ser12 and par96 designs. To - configure the driver, use the sethdlc utility available - in the standard ax25 utilities package. For information on the modems, - see http://www.baycom.de and drivers/net/README.baycom. + configure the driver, use the sethdlc utility available in the + standard ax25 utilities package. For information on the modems, see + http://www.baycom.de and drivers/net/README.baycom. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. This is recommended. @@ -2299,18 +2311,16 @@ Sound card modem driver for AX.25 CONFIG_SOUNDMODEM This experimental driver allows a standard SoundBlaster or - WindowsSoundSystem compatible sound card to be used as a packet radio - modem (NOT as a telephone modem!), to send digital traffic over - amateur radio. - + WindowsSoundSystem compatible sound card to be used as a packet + radio modem (NOT as a telephone modem!), to send digital traffic + over amateur radio. To configure the driver, use the sethdlc, smdiag and smmixer utilities available in the standard ax25 utilities package. For information on how to key the transmitter, see http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html (to browse the WWW, you need to have access to a machine on the Internet that has a program like lynx or netscape) and - Documentation/networking/soundmodem.txt. - + Documentation/networking/soundmodem.txt. If you want to compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. This is recommended. @@ -2322,8 +2332,8 @@ compatible cards. If you have a dual mode card (i.e. a WSS cards with a SoundBlaster emulation) you should say N here and Y to "Sound card modem support for WSS and Crystal cards", below, because - this usually results in better performance. This option also supports - SB16/32/64 in full duplex mode. + this usually results in better performance. This option also + supports SB16/32/64 in full duplex mode. Sound card modem support for WSS and Crystal cards CONFIG_SOUNDMODEM_WSS @@ -2337,15 +2347,16 @@ Sound card modem support for 1200 baud AFSK modulation CONFIG_SOUNDMODEM_AFSK1200 - This option enables the soundmodem driver 1200 baud AFSK modem, - compatible to popular modems using TCM3105 or AM7911. The demodulator - requires about 12% of the CPU power of a Pentium 75 CPU per channel. + This option enables the soundmodem driver 1200 baud AFSK modem, + compatible to popular modems using TCM3105 or AM7911. + The demodulator requires about 12% of the CPU power of a Pentium 75 + CPU per channel. Sound card modem support for 2400 baud AFSK modulation (7.3728MHz crystal) CONFIG_SOUNDMODEM_AFSK2400_7 This option enables the soundmodem driver 2400 baud AFSK modem, compatible to TCM3105 modems (over-)clocked with a 7.3728MHz - crystal. Note that the availability of this driver does _not_ imply + crystal. Note that the availability of this driver does _not_ imply that I recommend building such links. It is only here since users especially in eastern Europe have asked me to do so. In fact this modulation scheme has many disadvantages, mainly its incompatibility @@ -2356,8 +2367,8 @@ CONFIG_SOUNDMODEM_AFSK2400_8 This option enables the soundmodem driver 2400 baud AFSK modem, compatible to TCM3105 modems (over-)clocked with an 8MHz crystal. - Note that the availability of this driver does _not_ imply that I - recommend building such links. It is only here since users + Note that the availability of this driver does _not_ imply that + I recommend building such links. It is only here since users especially in eastern Europe have asked me to do so. In fact this modulation scheme has many disadvantages, mainly its incompatibility with many transceiver designs and the fact that the TCM3105 (if @@ -2405,29 +2416,31 @@ software installed, e.g. the Crynwr PLIP packet driver (http://sunsite.cnam.fr/packages/Telnet/PC/msdos/misc/pktdrvr.txt) and winsock or NCSA's telnet. If you want to use this, say Y and - read the PLIP mini-HOWTO, available via ftp (user: anonymous) in + read the PLIP mini-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini as well as the NET-2-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the PLIP protocol was changed and this PLIP driver won't work together with the PLIP support in Linux versions 1.0.x. This option enlarges - your kernel by about 8kB. If you want to compile this as a module ( - = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read Documentation/modules.txt as - well as Documentation/networking/net-modules.txt. If you want to use - both a parallel printer and PLIP, there are two cases: 1) If the - printer and the PLIP cable are to use the same parallel port - (presumably because you have just one), it is best to compile both - drivers as modules and load and unload them as needed. 2) To use - different parallel ports for the printer and the PLIP cable, you can - say Y to the printer driver, specify the base address of the - parallel port(s) to use for the printer(s) with the "lp" kernel - command line option. (See the documentation of your boot loader - (lilo or loadlin) about how to pass options to the kernel at boot - time. The lilo procedure is also explained in the SCSI-HOWTO, - available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard base addresses - as well as the syntax of the "lp" command line option can be found - in drivers/char/lp.c. You can then say Y to this PLIP driver or, + your kernel by about 8kB. + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. + If you want to use both a parallel printer and PLIP, there are two + cases: + 1) If the printer and the PLIP cable are to use the same parallel + port (presumably because you have just one), it is best to compile + both drivers as modules and load and unload them as needed. + 2) To use different parallel ports for the printer and the PLIP + cable, you can say Y to the printer driver, specify the base address + of the parallel port(s) to use for the printer(s) with the "lp" + kernel command line option. (See the documentation of your boot + loader (lilo or loadlin) about how to pass options to the kernel at + boot time. The lilo procedure is also explained in the SCSI-HOWTO, + available via FTP (user: anonymous) in sunsite.unc.edu: + /pub/Linux/docs/HOWTO.) The standard base addresses as well as the + syntax of the "lp" command line option can be found in + drivers/char/lp.c. You can then say Y to this PLIP driver or, preferably, M in which case Documentation/networking/net-modules.txt tells you how to specify the port and IRQ to be used by PLIP at module load time. @@ -2437,8 +2450,8 @@ CONFIG_EQUALIZER If you have two serial connections to some other computer (this usually requires two modems and two telephone lines) and you use - SLIP (= the protocol for sending internet traffic over telephone - lines) or PPP (= a better SLIP) on them, you can make them behave + SLIP ( = the protocol for sending internet traffic over telephone + lines) or PPP ( = a better SLIP) on them, you can make them behave like one double speed connection using this driver. Naturally, this has to be supported at the other end as well, either with a similar EQL Linux driver or with a Livingston Portmaster 2e. Say Y if you @@ -2450,7 +2463,7 @@ Frame Relay (DLCI) support CONFIG_DLCI This is support for the frame relay protocol; frame relay is a fast - low-cost way to connect to a remote internet access provider or to + low-cost way to connect to a remote Internet access provider or to form a private wide area network. The one physical line from your box to the local "switch" (i.e. the entry point to the frame relay network, usually at the phone company) can carry several logical @@ -2491,53 +2504,53 @@ Sun LANCE Ethernet support CONFIG_SUN_LANCE - This is support for lance ethernet cards on Sun workstations such as - the Sparcstation IPC (any Sparc with a network interface 'le0' under + This is support for lance Ethernet cards on Sun workstations such as + the SPARCstation IPC (any SPARC with a network interface 'le0' under SunOS basically). Sun Intel Ethernet support CONFIG_SUN_INTEL - This is support for the intel ethernet cards on some Sun + This is support for the Intel Ethernet cards on some Sun workstations (all those with a network interface 'ie0' under SunOS). Western Digital/SMC cards CONFIG_NET_VENDOR_SMC - If you have a network (ethernet) card belonging to this class, say Y - and read the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than - one network card under linux, read the Multiple-Ethernet-mini-HOWTO, + one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the questions about Western Digital cards. If you say Y, you will be - asked for your specific card in the following questions. If you plan - to use more than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + asked for your specific card in the following questions. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. WD80*3 support CONFIG_WD80x3 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. SMC Ultra support CONFIG_ULTRA - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use - more than one network card under linux, read the + more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Important: There have been many reports that, with some motherboards @@ -2547,14 +2560,14 @@ SMC Ultra32 support CONFIG_ULTRA32 This is support for the SMC Ultra32 EISA card in shared memory mode. - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use - more than one network card under linux, read the + more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. @@ -2563,101 +2576,101 @@ This is support for the SMC9xxx based Ethernet cards. Choose this option if you have a DELL laptop with the docking station, or another SMC9192/9194 based chipset. Say Y if you want it compiled - into the kernel, and read the the file drivers/net/README.smc9 and - the Ethernet-HOWTO, available via ftp (user: anonymous) in + into the kernel, and read the file drivers/net/README.smc9 and + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use - more than one network card under linux, read the + more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. AMD LANCE and PCnet (AT1500 and NE2100) support CONFIG_LANCE - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than - one network card under linux, read the Multiple-Ethernet-mini-HOWTO, + one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 3COM cards CONFIG_NET_VENDOR_3COM - If you have a network (ethernet) card belonging to this class, say Y - and read the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the questions about 3COM cards. If you say Y, you will be asked for your specific card in the following questions. If you plan to use more than one network card - under linux, read the Multiple-Ethernet-mini-HOWTO, available from + under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 3c501 support CONFIG_EL1 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, consider buying a new card, since the 3c501 is slow and obsolete. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. - If you plan to use more than one network card under linux, + If you plan to use more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini---and don't use 3c501s. + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini -- and don't use 3c501s. 3c503 support CONFIG_EL2 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. - If you plan to use more than one network card under linux, + If you plan to use more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 3c505 support CONFIG_ELPLUS - Information about this network (ethernet) card can be found in + Information about this network (Ethernet) card can be found in Documentation/networking/3c505.txt. If you have a card of this type, - say Y and read the Ethernet-HOWTO, available via ftp (user: + say Y and read the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. - If you plan to use more than one network card under linux, + If you plan to use more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 3c507 support CONFIG_EL16 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 3c509/3c579 support CONFIG_EL3 - If you have a network (ethernet) card belonging to the 3Com + If you have a network (Ethernet) card belonging to the 3Com EtherLinkIII series, say Y and read the Ethernet-HOWTO, available - via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. + via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the + than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If your card is not working you may need to use the DOS setup disk to disable Plug & @@ -2665,8 +2678,8 @@ 3c590 series (592/595/597) "Vortex" support CONFIG_VORTEX - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is in Documentation/networking/vortex.txt and in the comments at the beginning of drivers/net/3c59x.c. If you want to compile this @@ -2674,23 +2687,23 @@ running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the + than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Other ISA cards CONFIG_NET_ISA - If your network (ethernet) card hasn't been mentioned yet and its + If your network (Ethernet) card hasn't been mentioned yet and its bus system (that's the way the components of the card talk to each other) is ISA (as opposed to EISA, VLB or PCI), say Y. Make sure you know the name of your card. Read the Ethernet-HOWTO, available via - ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If + FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If unsure, say Y. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the remaining ISA network card questions. If you say Y, you will be asked for your specific card in the following questions. If you plan to use more than one network card under - linux, read the Multiple-Ethernet-mini-HOWTO, available from + Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. ARCnet support @@ -2698,19 +2711,19 @@ If you have a network card of this type, say Y and check out the (arguably) beautiful poetry in Documentation/networking/arcnet.txt. You might also want to have a look at the Ethernet-HOWTO, available - via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO - (even though ARCnet is not really ethernet). This driver is also + via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO + (even though ARCnet is not really Ethernet). This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to - use more than one network card under linux, read the + use more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Enable arc0e (ARCnet "ether-encap" packet format) CONFIG_ARCNET_ETH - This allows you to use "ethernet encapsulation" with your ARCnet + This allows you to use "Ethernet encapsulation" with your ARCnet card via the virtual arc0e device. You only need arc0e if you want to talk to nonstandard ARCnet software, specifically, DOS/Windows-style "NDIS" drivers. You do not need to enable this @@ -2736,77 +2749,77 @@ Cabletron E21xx support CONFIG_E2100 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. DEPCA support CONFIG_DEPCA - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO as well as - drivers/net/depca.c. If you want to compile this as a module ( = - code which can be inserted in and removed from the running kernel + drivers/net/depca.c. If you want to compile this as a module + (= code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use - more than one network card under linux, read the + more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. EtherWorks 3 support CONFIG_EWRK3 - This driver supports the DE203, DE204 and DE205 network (ethernet) + This driver supports the DE203, DE204 and DE205 network (Ethernet) cards. If this is for you, say Y and read drivers/net/README.ewrk3 in the kernel source as well as the Ethernet-HOWTO, available via - ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. + FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the + than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. SEEQ8005 support CONFIG_SEEQ8005 - This is a driver for the SEEQ 8005 network (ethernet) card. If this - is for you, read the Ethernet-HOWTO, available via ftp (user: + This is a driver for the SEEQ 8005 network (Ethernet) card. If this + is for you, read the Ethernet-HOWTO, available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan - to use more than one network card under linux, read the + to use more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. AT1700 support CONFIG_AT1700 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. FMV-181/182/183/184 support CONFIG_FMV18X - If you have a Fujitsu FMV-181/182/183/184 network (ethernet) card, - say Y and read the Ethernet-HOWTO, available via ftp (user: + If you have a Fujitsu FMV-181/182/183/184 network (Ethernet) card, + say Y and read the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. - If you plan to use more than one network card under linux, + If you plan to use more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you use FMV-183 or FMV-184 and it is not working, you may need @@ -2820,21 +2833,21 @@ EtherExpressPro support CONFIG_EEXPRESS_PRO - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. EtherExpress support CONFIG_EEXPRESS - If you have an EtherExpress16 network (ethernet) card, say Y and - read the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have an EtherExpress16 network (Ethernet) card, say Y and + read the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the Intel EtherExpress16 card used to be regarded as a very poor choice because the driver was very unreliable. We now have a new driver @@ -2843,24 +2856,24 @@ running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the + than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. NI5210 support CONFIG_NI52 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than - one network card under linux, read the Multiple-Ethernet-mini-HOWTO, + one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. NI6510 support CONFIG_NI65 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than - one network card under linux, read the Multiple-Ethernet-mini-HOWTO, + one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Ottawa PI and PI/2 support @@ -2871,7 +2884,7 @@ http://hydra.carleton.ca/info/pi2.html (To browse the WWW, you need to have access to a machine on the Internet that has one of the programs lynx, netscape or Mosaic). If you have one of these cards, - you can say Y here and should read the HAM-HOWTO, available via ftp + you can say Y here and should read the HAM-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you should have said Y to "AX.25 support" above, because AX.25 is the protocol used for digital traffic over radio links. @@ -2888,7 +2901,7 @@ AT&T WaveLAN & DEC RoamAbout DS support CONFIG_WAVELAN The Lucent Wavelan (formerly NCR and AT&T ; or DEC RoamAbout DS) - is a Radio LAN (wireless ethernet-like) at 900 MHz and 2.4 GHz. + is a Radio LAN (wireless Ethernet-like) at 900 MHz and 2.4 GHz. This driver support the ISA version of the Wavelan. A driver for the pcmcia hardware is available in David Hinds's pcmcia package. This driver is fairly stable and may be compiled as a module @@ -2902,129 +2915,129 @@ HP PCLAN+ (27247B and 27252A) support CONFIG_HPLAN_PLUS - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. HP PCLAN (27245 and other 27xxx series) support CONFIG_HPLAN - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. HP 10/100VG PCLAN (ISA, EISA, PCI) support CONFIG_HP100 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the + than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. NE2000/NE1000 support CONFIG_NE2000 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. SK_G16 support CONFIG_SK_G16 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than - one network card under linux, read the Multiple-Ethernet-mini-HOWTO, + one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. EISA, VLB, PCI and on board controllers CONFIG_NET_EISA This is another class of network cards which attach directly to the bus. If you have one of those, say Y and read the Ethernet-HOWTO, - available via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/docs/HOWTO; if you are unsure, say - Y. Note that the answer to this question doesn't directly affect the + available via FTP (user: anonymous) from sunsite.unc.edu: + /pub/Linux/docs/HOWTO; if you are unsure, say Y. + Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the questions about this class of network cards. If you say Y, you - will be asked for your specific card in the following questions. If - you plan to use more than one network card under linux, read the + will be asked for your specific card in the following questions. + If you plan to use more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Ansel Communications EISA 3200 support CONFIG_AC3200 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. -Apricot Xen-II on board ethernet +Apricot Xen-II on board Ethernet CONFIG_APRICOT - If you have a network (ethernet) controller of this type, say Y and - read the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) controller of this type, say Y and + read the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the + than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. DE425, DE434, DE435 support CONFIG_DE4X5 - This is support for the DIGITAL series of PCI/EISA ethernet + This is support for the DIGITAL series of PCI/EISA Ethernet cards. These include the DE425, DE434, DE435, DE450 and DE500 models. If you have a network card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is contained in drivers/net/README.de4x5. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. DECchip Tulip (dc21x4x) PCI support CONFIG_DEC_ELCP - This driver is developed for the SMC EtherPower series ethernet + This driver is developed for the SMC EtherPower series Ethernet cards and also works with cards based on the DECchip 21040/21041/21140 (Tulip series) chips. (If your card is NOT SMC EtherPower 10/100 PCI (smc9332dst), you can also try the driver from "DE425, DE434, DE435 support", above.) However, most people with a network card of this type will say Y here. Do read the - Ethernet-HOWTO, available via ftp (user: anonymous) in + Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is contained in Documentation/networking/tulip.txt. This driver is also available as a module ( = code which can be inserted in and removed @@ -3035,33 +3048,33 @@ Digi Intl. RightSwitch support CONFIG_DGRS This is support for the Digi International RightSwitch series of - PCI/EISA ethernet switch cards. These include the SE-4 and the SE-6 + PCI/EISA Ethernet switch cards. These include the SE-4 and the SE-6 models. If you have a network card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is contained in drivers/net/README.dgrs. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. ICL EtherTeam 16i/32 support CONFIG_ETH16I - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in + If you have a network (Ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under Linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. -TI ThunderLAN support (EXPERIMENTAL) +TI ThunderLAN support CONFIG_TLAN If you have a TLAN based network card which is supported by this driver, say Y and read the Ethernet-HOWTO. Devices currently @@ -3069,19 +3082,25 @@ Internal NetFlex 3. This driver is also available as a module. Please email feedback to james.banks@caldera.com. +VIA Rhine support +CONFIG_VIA_RHINE + If you have a VIA "rhine" based network card (Rhine-I (3043) or + Rhine-2 (VT86c100A)), say Y here. To build this driver as a module + say M. + Zenith Z-Note support CONFIG_ZNET The Zenith Z-Note notebook computer has a built-in network - (ethernet) card, and this is the Linux driver for it. Note that the + (Ethernet) card, and this is the Linux driver for it. Note that the IBM Thinkpad 300 is compatible with the Z-Note and is also supported - by this driver. Read the Ethernet-HOWTO, available via ftp (user: + by this driver. Read the Ethernet-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Pocket and portable adapters CONFIG_NET_POCKET - Cute little network (ethernet) devices which attach to the parallel + Cute little network (Ethernet) devices which attach to the parallel port ("pocket adapters"), commonly used with laptops. If you have - one of those, say Y and read the Ethernet-HOWTO, available via ftp + one of those, say Y and read the Ethernet-HOWTO, available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to plug a network card into the PCMCIA slot of your laptop instead (PCMCIA is the standard for credit card size extension cards @@ -3091,7 +3110,7 @@ will just cause this configure script to skip all the questions about this class of network devices. If you say Y, you will be asked for your specific device in the following questions. If you - plan to use more than one network device under linux, read the + plan to use more than one network device under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you intend to use an adapter attaching to the parallel port as well as a parallel @@ -3099,11 +3118,11 @@ AT-LAN-TEC/RealTek pocket adapter support CONFIG_ATP - This is a network (ethernet) device which attaches to your parallel + This is a network (Ethernet) device which attaches to your parallel port. Read drivers/net/atp.c as well as the Ethernet-HOWTO, - available via ftp (user: anonymous) from + available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. If - you plan to use more than one network card under linux, read the + you plan to use more than one network card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you intend to use this driver, you should have said N to the Parallel Printer support, @@ -3111,36 +3130,36 @@ D-Link DE600 pocket adapter support CONFIG_DE600 - This is a network (ethernet) device which attaches to your parallel + This is a network (Ethernet) device which attaches to your parallel port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO, - available via ftp (user: anonymous) from + available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. 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. If you intend to use this pocket adapter as well as a parallel printer, you should compile both drivers as modules. If you plan to use more than one network - card under linux, read the Multiple-Ethernet-mini-HOWTO, available + card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. D-Link DE620 pocket adapter support CONFIG_DE620 - This is a network (ethernet) device which attaches to your parallel + This is a network (Ethernet) device which attaches to your parallel port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO, - available via ftp (user: anonymous) from + available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. 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. If you intend to use this pocket adapter as well as a parallel printer, you should compile both drivers as modules. If you plan to use more than one network - card under linux, read the Multiple-Ethernet-mini-HOWTO, available + card under Linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Token Ring driver support CONFIG_TR Token Ring is IBM's way of communication on a local network; the - rest of the world uses ethernet. If you are connected to a token + rest of the world uses Ethernet. If you are connected to a token ring network and want to use your Token Ring card under Linux, say Y. Most people can say N here. @@ -3157,7 +3176,7 @@ Support non-SCSI/IDE/ATAPI drives CONFIG_CD_NO_IDESCSI If you have a CDROM drive that is neither SCSI nor IDE/ATAPI, say Y - here, otherwise N. Read the CDROM-HOWTO, available via ftp (user: + here, otherwise N. Read the CDROM-HOWTO, available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the questions @@ -3248,8 +3267,8 @@ CONFIG_AZTCD This is your driver if you have an Aztech CDA268-01A, Orchid CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCDROM CR520 or - CR540 CDROM drive. This driver - just like all these CDROM drivers - - is NOT for CDROM drives with IDE/ATAPI interface, such as Aztech + CR540 CDROM drive. This driver -- just like all these CDROM drivers + -- is NOT for CDROM drives with IDE/ATAPI interface, such as Aztech CDA269-031SE. Sony CDU535 CDROM support @@ -3298,8 +3317,8 @@ at boot time, please say Y. Boot time command line options (or 'append=' options in /etc/lilo.conf) are: isp16=,,, - Here 'port','irq' and 'dma' are the base i/o address, irq number and - dma line assumed to be used by the attached cdrom + Here 'port','irq' and 'dma' are the base i/o address, IRQ number and + DMA line assumed to be used by the attached cdrom drive. 'drive_type' is the type of cdrom drive or its emulation mode. Valid values for drive_type include: Sanyo, Panasonic (same as Sanyo), Sony and Mitsumi. Default values are: port=0x340, irq=0, @@ -3316,8 +3335,8 @@ usage (also called diskquotas). Currently, it works only for the ext2 filesystem. You need additional software in order to use quota support; check the file Documentation/Changes for that. Probably the - quota support is only useful for multi user systems. If unsure, say - N. + quota support is only useful for multi user systems. If unsure, + say N. Mandatory lock support CONFIG_LOCK_MANDATORY @@ -3333,56 +3352,56 @@ Minix fs support CONFIG_MINIX_FS - Minix is a simple operating system used in many classes about - OS's. The minix filesystem (= method to organize files on a harddisk + Minix is a simple operating system used in many classes about OS's. + The minix filesystem ( = method to organize files on a hard disk partition or a floppy disk) was the original filesystem for Linux, has been superseded by the second extended filesystem ext2fs but is still used for root/boot and other floppies or ram disks since it is - leaner. You don't want to use it on your harddisk because of certain - built-in restrictions. This option will enlarge your kernel by about - 25 kB. Everyone should say Y or M so that they are able to read this - common floppy format. If you want to compile this as a module - ( = code which can be inserted in and removed from the + leaner. You don't want to use it on your hard disk because of + certain built-in restrictions. This option will enlarge your kernel + by about 25 kB. Everyone should say Y or M so that they are able to + read this common floppy format. 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. Note that the filesystem of your root partition cannot be compiled as a module. Extended fs support CONFIG_EXT_FS - This is the old Linux filesystem (= method to organize files on a - harddisk partition or a floppy disk) and not in use anymore. It - enlarges your kernel by about 25 kB. Let's all kill this beast. Say - N. + This is the old Linux filesystem ( = method to organize files on a + hard disk partition or a floppy disk) and not in use anymore. + It enlarges your kernel by about 25 kB. Let's all kill this beast. + Say N. Second extended fs support CONFIG_EXT2_FS - This is the de facto standard Linux filesystem (= method to organize - files on a storage device) for harddisks. You want to say Y, unless - you intend to use Linux exclusively from inside a DOS partition - using the umsdos filesystem. The advantage of the latter is that you - can get away without repartitioning your hard drive (which often - implies backing everything up and restoring afterwards); the - disadvantage is that Linux becomes susceptible to DOS viruses and - that umsdos is somewhat slower than ext2fs. Even if you want to run - Linux in this fashion, it might be a good idea to have ext2fs - around: it enables you to read more floppy disks and facilitates the - transition to a *real* Linux partition later. Another (rare) case - which doesn't require ext2fs is a diskless Linux box which mounts - all files over the network using NFS (in this case it's sufficient - to enable NFS filesystem support below; if you are planning to do - this, have a look at the netboot package in - /pub/Linux/system/Linux-boot/, available via ftp (user: anonymous) + This is the de facto standard Linux filesystem ( = method to + organize files on a storage device) for hard disks. You want to + say Y, unless you intend to use Linux exclusively from inside a DOS + partition using the umsdos filesystem. The advantage of the latter + is that you can get away without repartitioning your hard drive + (which often implies backing everything up and restoring + afterwards); the disadvantage is that Linux becomes susceptible to + DOS viruses and that umsdos is somewhat slower than ext2fs. Even if + you want to run Linux in this fashion, it might be a good idea to + have ext2fs around: it enables you to read more floppy disks and + facilitates the transition to a *real* Linux partition later. + Another (rare) case which doesn't require ext2fs is a diskless Linux + box which mounts all files over the network using NFS (in this case + it's sufficient to enable NFS filesystem support below; if you are + planning to do this, have a look at the netboot package in + /pub/Linux/system/Linux-boot/, available via FTP (user: anonymous) from sunsite.unc.edu, extract with "tar xzvf filename"). There is a - short ext2fs-FAQ, available via ftp (user: anonymous) in + short ext2fs-FAQ, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/faqs. This option will enlarge your kernel by about 41 kB. Default is Y. xiafs filesystem support CONFIG_XIA_FS - This is an old filesystem (= method to organize files on a harddisk - partition or a floppy disk) and not in use anymore. This option + This is an old filesystem ( = method to organize files on a hard + disk partition or a floppy disk) and not in use anymore. This option would enlarge your kernel by about 28 kB. Let's all kill this beast: - say N. If you want to compile this as a module ( = code which can + say N. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. Note that the filesystem of your root partition cannot be compiled as a module. @@ -3397,16 +3416,16 @@ this as a module however ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. Note that if you compile the FAT - support as a module, you cannot compile any of the FAT-based file- - systems into the kernel - they will have to be modules as well. + support as a module, you cannot compile any of the FAT-based + filesystems into the kernel -- they will have to be modules as well. The filesystem of your root partition cannot be a module. msdos fs support CONFIG_MSDOS_FS - This allows you to mount MSDOS partitions of your harddrive (unless + This allows you to mount MSDOS partitions of your hard drive (unless they are compressed; to access compressed MSDOS partitions under Linux, you can either use the DOS emulator DOSEMU, described in the - DOSEMU-HOWTO, available via ftp (user: anonymous) at + DOSEMU-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO, or try dmsdosfs in sunsite.unc.edu:/pub/Linux/system/Filesystems/dosfs. If you intend to use dosemu with a non-compressed MSDOS partition, say Y here) and @@ -3430,7 +3449,7 @@ vfat fs support CONFIG_VFAT_FS - This allows you to mount MSDOS partitions of your harddrive. It + This allows you to mount MSDOS partitions of your hard drive. It will let you use filenames in a way compatible with the long filenames used by Windows'95 and Windows NT fat-based (not NTFS) partitions. It does not support Windows'95 compressed filesystems. @@ -3445,7 +3464,7 @@ umsdos: Unix like fs on top of std MSDOS fs CONFIG_UMSDOS_FS Say Y here if you want to run Linux from within an existing DOS - partition of your harddrive. The advantage of this is that you can + partition of your hard drive. The advantage of this is that you can get away without repartitioning your hard drive (which often implies backing everything up and restoring afterwards) and hence you're able to quickly try out Linux or show it to your friends; the @@ -3459,9 +3478,8 @@ enabled both "fat fs support" and "msdos fs support" above. If unsure, say N. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read - Documentation/modules.txt. Note that the filesystem of your root - partition cannot be a module. + whenever you want), say M here and read Documentation/modules.txt. + Note that the filesystem of your root partition cannot be a module. nls: Native language codepages and Unicode support CONFIG_NLS @@ -3557,7 +3575,7 @@ nls codepage 850 CONFIG_NLS_CODEPAGE_850 - This is the DOS codepage that is used in much of Europe--United + This is the DOS codepage that is used in much of Europe -- United Kingdom, Germany, Spain, Italy, and [add more countries here]. It has some characters useful to many European languages that are not part of codepage 437. @@ -3619,49 +3637,48 @@ CONFIG_PROC_FS This is a virtual filesystem providing information about the status of the system. "Virtual" means that it doesn't take up any space on - your harddisk: the files are created on the fly when you access + your hard disk: the files are created on the fly when you access them. Also, you cannot read the files with less: you need to use more or cat. The filesystem is explained in the Kernel Hacker's - Guide, available via ftp (user: anonymous) in + Guide, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/LDP and also on the proc(8) manpage ("man 8 proc"). This option will enlarge your kernel by about 18 kB. It's totally cool; for example, "cat /proc/interrupts" gives information about what the different IRQs are used for at the moment (there is a small number of Interrupt ReQuest lines in your computer - that are used by the periphery to gain the CPU's attention - often a - source of trouble if two devices are mistakenly configured to use + that are used by the periphery to gain the CPU's attention -- often + a source of trouble if two devices are mistakenly configured to use the same IRQ). Several programs depend on this, so everyone should say Y here. NFS filesystem support CONFIG_NFS_FS If you are connected to some other (usually local) Unix computer - (using SLIP, PLIP, PPP or ethernet) and want to mount files + (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing on that computer (the NFS server) using the Network File Sharing protocol, say Y. "Mounting files" means that the client can access the files with usual UNIX commands as if they were - sitting on the client's harddisk. For this to work, the server must + sitting on the client's hard disk. For this to work, the server must run the programs nfsd and mountd (but does not need to have NFS filesystem support enabled). NFS is explained in the Network - Administrator's Guide, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/LDP, and on its man page: "man - nfs". There is also a NFS-FAQ in - sunsite.unc.edu:/pub/Linux/docs/faqs which presumes that you know - the basics of NFS already. If you say Y here, you should have said Y - to TCP/IP networking also. This option would enlarge your kernel by - about 27 kB. This filesystem is also available as a module ( = code - which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. If you configure a diskless - machine which will mount its root filesystem over nfs, you cannot - compile this driver as a module. If you don't know what all this is - about, say N. + Administrator's Guide, available via FTP (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/LDP, and on its man page: "man nfs". + There is also a NFS-FAQ in sunsite.unc.edu:/pub/Linux/docs/faqs + which presumes that you know the basics of NFS already. If you say Y + here, you should have said Y to TCP/IP networking also. This option + would enlarge your kernel by about 27 kB. This filesystem is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. + If you configure a diskless machine which will mount its root + filesystem over NFS, you cannot compile this driver as a module. + If you don't know what all this is about, say N. Root file system on NFS CONFIG_ROOT_NFS If you want your Linux box to mount its whole root filesystem from some other computer over the net via NFS (presumably because your - box doesn't have a harddisk), say Y. Read Documentation/nfsroot.txt + box doesn't have a hard disk), say Y. Read Documentation/nfsroot.txt for details. Most people say N here. BOOTP support @@ -3695,19 +3712,19 @@ Unix filenames are also supported by this driver. If you have a CDROM drive and want to do more with it than just listen to audio CDs and watch its LEDs, say Y (and read the CDROM-HOWTO, available - via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/docs/HOWTO), thereby enlarging your - kernel by about 27 kB; otherwise say N. If you want to compile this - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want), say M here and read + via FTP (user: anonymous) from sunsite.unc.edu: + /pub/Linux/docs/HOWTO), thereby enlarging your kernel by about + 27 kB; otherwise say N. If you want to compile this as a module + ( = code which can be inserted in and removed from the running + kernel whenever you want), say M here and read Documentation/modules.txt. OS/2 HPFS filesystem support (read only) CONFIG_HPFS_FS OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS - is the filesystem used for organizing files on OS/2 harddisk + is the filesystem used for organizing files on OS/2 hard disk partitions. Say Y if you want to be able to read files from an OS/2 - HPFS partition of your harddrive. OS/2 floppies however are in + HPFS partition of your hard drive. OS/2 floppies however are in regular MSDOS format, so you don't need this option in order to be able to read them. Read Documentation/filesystems/hpfs.txt. This filesystem is also available as a module ( = code which can be @@ -3717,10 +3734,10 @@ System V and Coherent filesystem support CONFIG_SYSV_FS - SCO, Xenix and Coherent are commercial Unix systems for intel + SCO, Xenix and Coherent are commercial Unix systems for Intel machines. Enabling this option would allow you to read and write to - and from their floppies and harddisk partitions. If you have a - floppy or harddisk partition like that, it is probable that they + and from their floppies and hard disk partitions. If you have a + floppy or hard disk partition like that, it is probable that they contain binaries from those other Unix systems; in order to run these binaries, you will want to install iBCS2 (iBCS2 [Intel Binary Compatibility Standard] is a kernel module which lets you run SCO, @@ -3729,7 +3746,7 @@ WordPerfect. It's in tsx-11.mit.edu:/pub/linux/BETA). If you only intend to mount files from some other Unix over the network using NFS, you don't need the System V filesystem support (but you need - nfs filesystem support obviously). Note that this option is + NFS filesystem support obviously). Note that this option is generally not needed for floppies, since a good portable way to transport files and directories between unixes (and even other operating systems) is given by the tar program ("man tar"). Note @@ -3738,9 +3755,8 @@ Documentation/filesystems/sysv-fs.txt. This option will enlarge your kernel by about 34 kB. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel - whenever you want), say M here and read - Documentation/modules.txt. If you haven't heard about all of this - before, it's safe to say N. + whenever you want), say M here and read Documentation/modules.txt. + If you haven't heard about all of this before, it's safe to say N. Kernel automounter support (experimental) CONFIG_AUTOFS_FS @@ -3761,7 +3777,7 @@ as well. Enabling this option allows you to mount these partitions and diskettes read-only. If you only intend to mount files from some other Unix over the network using NFS, you don't need the - UFS filesystem support (but you need nfs filesystem support + UFS filesystem support (but you need NFS filesystem support obviously). Note that this option is generally not needed for floppies, since a good portable way to transport files and directories between unixes (and even other operating systems) @@ -3790,21 +3806,21 @@ incompatible with all others. Enabling this option allows you to read these partition tables and further mount SunOS disks on your Linux box if you also have configured BSD ufs filesystem support. - This is mainly used to carry data from a Sparc under SunOS to your + This is mainly used to carry data from a SPARC under SunOS to your Linux box via a removable medium like magneto-optical or ZIP drives. If you don't know what all this is about, say N. -SMB filesystem support (to mount WfW shares etc..) +SMB filesystem support (to mount WfW shares etc...) CONFIG_SMB_FS SMB (Server Message Buffer) is the protocol Windows for Workgroups (WfW), Windows NT and Lan Manager use to talk to each other over an - ethernet. Enabling this allows you to mount their filesystems and + Ethernet. Enabling this allows you to mount their filesystems and access them just like any other unix directory. For details, read Documentation/filesystems/smbfs.txt. Note: if you just want your box to act as an SMB *server* and make files and printing services available to Windows clients (which need to have a TCP/IP stack), you don't need to enable this filesystem support; you can use the - program samba (available via ftp (user: anonymous) in + program samba (available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/system/Network/samba) for that. General information about how to connect Linux, Windows machines and Macs is on the WWW at http://eats.com/linux_mac_win.html (to browse the WWW, @@ -3825,7 +3841,7 @@ CONFIG_NCP_FS NCP (NetWare Core Protocol) is a protocol that runs over IPX and is used by Novel NetWare clients to talk to file servers. It is to IPX - what nfs is to tcp/ip, if that helps. Enabling this option allows + what NFS is to TCP/IP, if that helps. Enabling this option allows you to mount NetWare file server volumes and to access them just like any other Unix directory. For details, please read the file Documentation/filesystems/ncpfs.txt in the kernel source and the @@ -3837,13 +3853,13 @@ Amiga FFS filesystem support (EXPERIMENTAL) CONFIG_AFFS_FS The Fast File System (FFS) is the common filesystem used on - harddisks by Amiga (tm) Systems since AmigaOS Version 1.3 (34.20). + hard disks by Amiga (tm) Systems since AmigaOS Version 1.3 (34.20). It's also possible to mount diskfiles used by the Un*X Amiga Emulator by Bernd Schmidt (http://www-users.informatik.rwth-aachen.de/~crux/uae.html). If you want to do the latter, you will also need the loop device support. Say Y if you want to be able to read and write files from - and to an Amiga FFS partition of your harddrive. Amiga floppies + and to an Amiga FFS partition of your hard drive. Amiga floppies however cannot be read with this driver due to an incompatibility of the floppy controller used in an Amiga and the standard floppy controller in PCs and workstations. Read @@ -3857,7 +3873,7 @@ CONFIG_SERIAL This selects whether you want to include the driver for the standard serial ports. People who might say N here are those that are - setting up dedicated ethernet WWW/ftp servers, or users that have + setting up dedicated Ethernet WWW/FTP servers, or users that have one of the various bus mice instead of a serial mouse. (Note that the Cyclades and Stallion multi serial port drivers do not need this driver built in for them to work. They are completely independent of @@ -3874,7 +3890,7 @@ CONFIG_DIGI This is a driver for the Digiboard PC/Xe, PC/Xi, and PC/Xeve cards that give you many serial ports. You would need something like this - to connect more than two modems to your linux box, for instance in + to connect more than two modems to your Linux box, for instance in order to become a BBS. If you have a card like that, say Y here and read the file Documentation/digiboard.txt. @@ -3882,16 +3898,34 @@ CONFIG_RISCOM8 This is a driver for the SDL Communications RISCom/8 multiport card, that give you many serial ports. You would need something like this - to connect more than two modems to your linux box, for instance in + to connect more than two modems to your Linux box, for instance in order to become a BBS. If you have a card like that, say Y here and read the file Documentation/riscom8.txt. Also it's possible to say M here and compile this driver as kernel loadable module. +Specialix IO8+ card support +CONFIG_SPECIALIX + This is a driver for the Specialix IO8+ multiport card (both the + ISA and the PCI version), that gives you 8 serial ports. You would + need a card like this to connect more than two modems to your linux + box, for instance in order to become a BBS. If you have a card like + that, say Y here and read the file Documentation/specialix.txt. Also + it's possible to say M here and compile this driver as kernel + loadable module. + +Specialix DTR/RTS pin is RTS +CONFIG_SPECIALIX_RTSCTS + The Specialix card can only support either RTS or DTR. When this + option is off, the driver will use the pin as "DTR" when the tty is + in software handshake mode. When this option is on or hardware + handshake is on, it will always be RTS. Read the file + Documentation/specialix.txt for more information. + Cyclades async mux support CONFIG_CYCLADES This is a driver for a card that gives you many serial ports. You would need something like this to connect more than two modems to - your linux box, for instance in order to become a BBS. If you want + your Linux box, for instance in order to become a BBS. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. If you haven't heard about it, it's @@ -3901,7 +3935,7 @@ Stallion multiport serial support CONFIG_STALDRV Stallion cards give you many serial ports. You would need something - like this to connect more than two modems to your linux box, for + like this to connect more than two modems to your Linux box, for instance in order to become a BBS. If you say Y here, you will be asked for your specific card model in the next questions. Make sure to read drivers/char/README.stallion in this case. If you have never @@ -3929,7 +3963,7 @@ If you intend to attach a printer to the parallel port of your Linux box (as opposed to using a serial printer; if the connector at the printer has 9 or 25 holes ["female"], then it's serial), say Y. Also - read the Printing-HOWTO, available via ftp (user: anonymous) in + read the Printing-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -3943,7 +3977,7 @@ the printer(s) with the "lp" kernel command line option. (See the documentation of your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time. The lilo procedure is also - explained in the SCSI-HOWTO, available via ftp (user: anonymous) in + explained in the SCSI-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard base addresses as well as the syntax of the "lp" command line option can be found in drivers/char/lp.c. You can then say Y to the PLIP driver or, @@ -3958,14 +3992,14 @@ a serial mouse. Most people have a regular serial MouseSystem or Microsoft mouse (made by Logitech) that plugs into a COM port (rectangular with 9 or 25 pins). These people say N here. If you - have something else, read the Busmouse-HOWTO, available via ftp + have something else, read the Busmouse-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO and say Y here. If you have a laptop, you either have to check the documentation or experiment a bit to find out whether the trackball is a serial mouse or not; it's best to say Y here for you. Note that - the answer to this question won't directly affect the kernel: saying - N will just cause this configure script to skip all the questions - about non-serial mice. If unsure, say Y. + the answer to this question won't directly affect the kernel: + saying N will just cause this configure script to skip all the + questions about non-serial mice. If unsure, say Y. Logitech busmouse support CONFIG_BUSMOUSE @@ -3973,7 +4007,7 @@ generally a round connector with 9 pins. Note that the newer mice made by Logitech don't use the Logitech protocol anymore; for those, you don't need this option. You want to read the Busmouse-HOWTO, - available via ftp (user: anonymous) in + available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read @@ -3990,7 +4024,7 @@ machines. The trackballs of some laptops are PS/2 mice also. In particular, the C&T 82C710 mouse on TI Travelmates is a PS/2 mouse. Although PS/2 mice are not technically bus mice, they are - explained in detail in the Busmouse-HOWTO, available via ftp (user: + explained in detail in the Busmouse-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and @@ -4001,27 +4035,27 @@ CONFIG_82C710_MOUSE This is a certain kind of PS/2 mouse used on the TI Travelmate. If you are unsure, try first to say N here and come back if the mouse - doesn't work. Read the Busmouse-HOWTO, available via ftp (user: + doesn't work. Read the Busmouse-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Microsoft busmouse support CONFIG_MS_BUSMOUSE These animals (also called Inport mice) are connected to an expansion board using a round connector with 9 pins. If this is what - you have, say Y and read the Busmouse-HOWTO, available via ftp + you have, say Y and read the Busmouse-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. If you are unsure, say N and read the HOWTO nevertheless: it will tell you what you have. Also be aware that several vendors talk about 'Microsoft busmouse' and - actually mean PS/2 busmouse - so count the pins on the connector. + actually mean PS/2 busmouse -- so count the pins on the connector. ATIXL busmouse support CONFIG_ATIXL_BUSMOUSE This is a rare type of busmouse that is connected to the back of an ATI video card. Note that most ATI mice are actually Microsoft - busmice. Read the Busmouse-HOWTO, available via ftp (user: + busmice. Read the Busmouse-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and @@ -4044,7 +4078,7 @@ CONFIG_QIC02_DYNCONF You can either configure this driver once and for all by editing a header file, in which case you should say N, or you can fetch a - program via anonymous ftp which is able to configure this driver + program via anonymous FTP which is able to configure this driver during runtime. If you want this, say Y. Ftape (QIC-80/Travan) support @@ -4065,8 +4099,8 @@ Zilog serial support CONFIG_SUN_ZS - This driver does not exist at this point, so you might as well say - N. + This driver does not exist at this point, so you might as well + say N. Advanced Power Management CONFIG_APM @@ -4127,7 +4161,7 @@ screen. Note that this is only used by the VC screen blanker, and won't turn off the backlight when using X11 (this also doesn't have anything to do with your VESA-compliant power-saving monitor). - Further, this option doesn't work for all laptops---it might not + Further, this option doesn't work for all laptops -- it might not turn off your backlight at all, or it might print a lot of errors to the console, especially if you are using gpm. @@ -4224,12 +4258,12 @@ computer. Every PC has such a clock built in. It can be used to generate signals from as low as 1Hz up to 8192Hz, and can also be used as a 24 hour alarm. It reports status information via the file - /proc/rtc and its behaviour is set by various ioctls on - /dev/rtc. People running SMP (= multiprocessor) versions of Linux - should enable this option to read and set the RTC clock in a SMP - compatible fashion. If you think you have a use for such a device - (such as periodic data sampling), then say Y here, and go read the - file Documentation/rtc.txt for details. + /proc/rtc and its behaviour is set by various ioctls on /dev/rtc. + People running SMP ( = multiprocessor) versions of Linux should + enable this option to read and set the RTC clock in a SMP compatible + fashion. If you think you have a use for such a device (such as + periodic data sampling), then say Y here, and go read the file + Documentation/rtc.txt for details. Sound card support CONFIG_SOUND @@ -4237,7 +4271,7 @@ than an occasional beep, say Y. Be sure to have all the information about your sound card and its configuration down (I/O port, interrupt and DMA channel), because you will be asked for it. You - want to read the Sound-HOWTO, available via ftp (user: anonymous) + want to read the Sound-HOWTO, available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. There is also some information in various README files in drivers/sound. If you want to compile this as a module ( = code which can be inserted in and @@ -4282,7 +4316,7 @@ MPU-401 support (NOT for SB16) CONFIG_MPU401 Be careful with this question. The MPU401 interface is supported by - all soundcards. However, some natively supported cards have their + all sound cards. However, some natively supported cards have their own driver for MPU401. Enabling the MPU401 option with these cards will cause a conflict. Also, enabling MPU401 on a system that doesn't really have a MPU401 could cause some trouble. If your card @@ -4338,7 +4372,7 @@ Ensoniq Soundscape support CONFIG_SSCAPE - Answer Y if you have a soundcard based on the Ensoniq SoundScape + Answer Y if you have a sound card based on the Ensoniq SoundScape chipset. Such cards are being manufactured at least by Ensoniq, Spea and Reveal (Reveal makes also other cards). @@ -4383,7 +4417,7 @@ Sun Audio support CONFIG_SUN_AUDIO - This is support for the soundcards on Sun workstations. The code + This is support for the sound cards on Sun workstations. The code does not exist yet, so you might as well say N here. Kernel profiling support @@ -4394,7 +4428,7 @@ it, you need the readprofile package from sunsite.unc.edu. Its manpage gives information regarding the format of profiling data. To become a kernel hacker, you can start with the Kernel Hacker's - Guide, available via ftp (user: anonymous) from + Guide, available via FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/LDP. Mere mortals say N. Profile shift count @@ -4436,8 +4470,8 @@ Support generic MP (RFC 1717) CONFIG_ISDN_MPP With synchronous PPP enabled, it is possible to increase throughput - by bundling several ISDN-connections, using this protocol. See - Documentation/isdn/README.syncppp for more information. + by bundling several ISDN-connections, using this protocol. + See Documentation/isdn/README.syncppp for more information. Use VJ-compression with synchronous PPP CONFIG_ISDN_PPP_VJ @@ -4470,92 +4504,152 @@ chipset in a more general way. This chipset is used on various ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, Teles S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and - many compatibles). It's a complete rewrite of the original Teles - driver. + many compatibles). It supports other chipsets too. See Documentation/isdn/README.HiSax for further informations on using this driver. +HiSax Support for EURO/DSS1 +CONFIG_HISAX_EURO + Enable this if you have a EURO ISDN line. + +Support for german chargeinfo +CONFIG_DE_AOC + If you have german AOC, you can enable this to get the charginfo. + +Disable sending complete +CONFIG_HISAX_NO_SENDCOMPLETE + If you have trouble with some ugly exchanges or you live in + Australia select this option. + +Disable sending low layer compatibility +CONFIG_HISAX_NO_LLC + If you have trouble with some ugly exchanges try to select this + option. + +HiSax Support for german 1TR6 +CONFIG_HISAX_1TR6 + Enable this if you have a old german 1TR6 line. + Note: Many older local switches are using 1TR6 on internal S0. + HiSax Support for Teles 16.0/8.0 CONFIG_HISAX_16_0 This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8 and many compatibles. - See Documentation/isdn/README.HiSax on how to configure it - using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + See Documentation/isdn/README.HiSax on how to configure it using the + different cards, a different D-channel protocol, or non-standard + IRQ/port/shmem settings. HiSax Support for Teles 16.3 or PNP or PCMCIA CONFIG_HISAX_16_3 This enables HiSax support for the Teles ISDN-cards S0-16.3 the Teles/Creatix PnP and the Teles PCMCIA. - See Documentation/isdn/README.HiSax on how to configure it - using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + See Documentation/isdn/README.HiSax on how to configure it using the + different cards, a different D-channel protocol, or non-standard + IRQ/port settings. + +HiSax Support for Teles 16.3c +CONFIG_HISAX_TELES3C + This enables HiSax support for the Teles 16.3c PnP. + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for Teles PCI +CONFIG_HISAX_TELESPCI + This enables HiSax support for the Teles PCI. + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for Teles S0Box +CONFIG_HISAX_S0BOX + This enables HiSax support for the Teles/Creatix parallel port + S0BOX. See Documentation/isdn/README.HiSax on how to configure it. HiSax Support for AVM A1 (Fritz) CONFIG_HISAX_AVM_A1 - This enables HiSax support for the AVM A1 (aka "Fritz"). - See Documentation/isdn/README.HiSax on how to configure it - using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + This enables HiSax support for the AVM A1 (aka "Fritz!"). + See Documentation/isdn/README.HiSax on how to configure it using the + different cards, a different D-channel protocol, or non-standard + IRQ/port settings. + +HiSax Support for AVM PnP/PCI (Fritz!PNP/PCI) +CONFIG_HISAX_FRITZPCI + This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI". + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for AVM A1 PCMCIA (Fritz) +CONFIG_HISAX_AVM_A1_PCMCIA + This enables HiSax support for the AVM A1 "Fritz!PCMCIA"). + See Documentation/isdn/README.HiSax on how to configure it. HiSax Support for Elsa ISA cards -CONFIG_HISAX_ELSA_PCC - This enables HiSax support for the Elsa Mircolink cards and - for the Elsa Quickstep series cards for the ISA bus. - You don't have to select "HiSax Support for Elsa PCMCIA card" - at the same time. - See Documentation/isdn/README.HiSax on how to configure it - using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. - -HiSax Support for Elsa PCMCIA card -CONFIG_HISAX_ELSA_PCMCIA - This enables HiSax support for the Elsa PCMCIA card. - You don't have to select "HiSax Support for Elsa ISA cards" at - the same time. - See Documentation/isdn/README.HiSax on how to configure it - using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. +CONFIG_HISAX_ELSA + This enables HiSax support for all Elsa cards. + See Documentation/isdn/README.HiSax on how to configure it using the + different cards, a different D-channel protocol, or non-standard + IRQ/port settings. + HiSax Support for ITK ix1-micro Revision 2 CONFIG_HISAX_IX1MICROR2 This enables HiSax support for the ITK ix1-micro Revision 2 card. - See Documentation/isdn/README.HiSax on how to configure it - using the different cards, a different D-channel protocol, or - non-standard irq/port/shmem settings. + See Documentation/isdn/README.HiSax on how to configure it. -HiSax Support for EURO/DSS1 -CONFIG_HISAX_EURO - You should choose your D-channel protocol your local - telephone service provider uses here by saying Y or N. - NOTE: This is mutually exclusive with HiSax Support for - german 1TR6 and US/NI-1 if you have only one ISDN card - installed. - -HiSax Support for US/NI-1 -CONFIG_HISAX_NI1 - You should choose your D-channel protocol your local - telephone service provider uses here by saying Y or N. - NOTE: This is mutually exclusive with HiSax Support for - german 1TR6 and EURO/DSS1 if you have only one ISDN card - installed. (not working yet, under developement) - -HiSax Support for german 1TR6 -CONFIG_HISAX_1TR6 - You should choose your D-channel protocol your local - telephone service provider uses here by saying Y or N. - NOTE: This is mutually exclusive with HiSax Support for - EURO/DSS1 and US/NI-1 if you have only one ISDN card - installed. +HiSax Support for Eicon.Diehl Diva cards +CONFIG_HISAX_DIEHLDIVA + This enables HiSax support for all none Pro versions of + Eicon.Diehl's Diva series passiv cards. + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for ASUSCOM cards +CONFIG_HISAX_ASUSCOM + This enables HiSax support for all passiv AsusCom and AsusComs OEM cards, + e.g PCBIT-DP, Dynalink and much more. + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for TELEINT cards +CONFIG_HISAX_TELEINT + This enables HiSax support for TeleInts semi-activ card and for + other HFC-2BS0 based cards. + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for Sedlbauer cards +CONFIG_HISAX_SEDLBAUER + This enables HiSax support for all Sedlbauer passiv cards such as + Sedlbauer Speed Card (Speed Win, Teledat 100), Speed Star + and Speed Star2 (PCMCIA), ISDN-Controller PC/104, Speed PCI and + Speed Fax+. + + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for USR Sportster internal TA +CONFIG_HISAX_SPORTSTER + This enables HiSax support for the USR (3Com) Sportster internal TA + passiv card. + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for MIC card +CONFIG_HISAX_MIC + This enables HiSax support for the MIC passiv card. + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for NETjet card +CONFIG_HISAX_NETJET + This enables HiSax support for the NetJet PCI and maybe for other + Tiger300 based passiv cards. + See Documentation/isdn/README.HiSax on how to configure it. + +HiSax Support for Niccy PnP/PCI card +CONFIG_HISAX_NICCY + This enables HiSax support for Dr. Neuhaus (Sagem) Niccy series + passiv cards. + See Documentation/isdn/README.HiSax on how to configure it. PCBIT-D support CONFIG_ISDN_DRV_PCBIT This enables support for the PCBIT ISDN-cards. This card is manufactured in Portugal by Octal. For running this card, additional firmware is necessary, which has to be downloaded into the card - using a utility which is distributed separately. See - Documentation/isdn/README and Documentation/isdn/README.pcbit for - more information. + using a utility which is distributed separately. + See Documentation/isdn/README and Documentation/isdn/README.pcbit + for more information. Spellcaster support (EXPERIMENTAL) CONFIG_ISDN_DRV_SC @@ -4574,25 +4668,32 @@ Support for AP1000 multicomputer CONFIG_AP1000 - This enables support for a sparc based parallel multi-computer + This enables support for a SPARC based parallel multi-computer called an AP1000+. For details on our efforts to port Linux to this machine see http://cap.anu.edu.au/cap/projects/linux or mail to hackers@cafe.anu.edu.au -Sparc ESP SCSI support +SPARC ESP SCSI support CONFIG_SCSI_SUNESP This is the driver for the Sun ESP SCSI host adapter. The ESP chipset is present in most SPARC-based computers. -Sparc /dev/openprom compatibility driver +SPARC /dev/openprom compatibility driver CONFIG_SUN_OPENPROMIO - This driver provides user programs with an interface to the Sparc + This driver provides user programs with an interface to the SPARC PROM device tree. The driver implements a SunOS-compatible interface and a NetBSD-compatible interface. 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 and read Documentation/modules.txt. If unsure, say Y. +Handle buggy SMP BIOSes with bad MTRR setup +CONFIG_MTRR + Some BIOSes for MP1.1/MP1.4 SMP machines fail to set the MTRR + registers in the chips up correctly as the specification and Intel + rules require. If you have a PPro or later SMP and one or more CPU's + report a value of about 2-3 bogomips enable this. + # need an empty line after last entry, for sed script in Configure. # @@ -4600,16 +4701,16 @@ # # LocalWords: CONFIG coprocessor DX Pentium SX lilo loadlin HOWTO ftp sunsite # LocalWords: unc edu docs emu README kB BLK DEV FD Thinkpad fd MFM RLL IDE gz -# LocalWords: cdrom harddisk diskless netboot nfs xzvf ATAPI MB harddrives ide -# LocalWords: HD harddisks CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios +# LocalWords: cdrom diskless netboot nfs xzvf ATAPI MB ide +# LocalWords: HD CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios # LocalWords: ISA EISA Microchannel VESA BIOSes IPC SYSVIPC ipc Ctrl dmesg hlt -# LocalWords: BINFMT Linkable http ac uk jo html GCC Sparc AVANTI CABRIOLET EB +# LocalWords: BINFMT Linkable http ac uk jo html GCC SPARC AVANTI CABRIOLET EB # LocalWords: netscape gcc LD CC toplevel MODVERSIONS insmod rmmod modprobe IP -# LocalWords: genksyms INET loopback gatewaying ethernet internet PPP ARP Arp +# LocalWords: genksyms INET loopback gatewaying Ethernet internet PPP ARP Arp # LocalWords: howto multicasting MULTICAST MBONE firewalling ipfw ACCT resp ip # LocalWords: proc acct IPIP encapsulator decapsulator klogd PCTCP RARP EXT PS # LocalWords: telneting subnetted NAGLE rlogin NOSR ttyS TGA techinfo mbone nl -# LocalWords: Mb SKB IPX Novell Netware dosemu Appletalk DDP ATALK tapedrive +# LocalWords: Mb SKB IPX Novell NetWare dosemu AppleTalk DDP ATALK tapedrive # LocalWords: SD CHR scsi thingy SG CD LUNs LUN jukebox Adaptec BusLogic EATA # LocalWords: buslogic DMA DPT ATT eata dma PIO UltraStor fdomain umsdos ext # LocalWords: QLOGIC qlogic TMC seagate Trantor ultrastor FASST wd NETDEVICES @@ -4622,26 +4723,26 @@ # LocalWords: TR Sony CDU caddyless cdu Mitsumi MCD cd mcd XA MultiSession CDA # LocalWords: Matsushita Panasonic SBPCD Soundblaster Longshine sbpcd Aztech # LocalWords: Okano Wearnes AZTCD CDD SE aztcd sonycd Goldstar GSCD Philips fs -# LocalWords: LMS OPTCD Sanyo SJCD minix faqs xiafs XIA msdos harddrive mtools +# LocalWords: LMS OPTCD Sanyo SJCD minix faqs xiafs XIA msdos mtools # LocalWords: std softlinks umssync NetworkFileSharing nfsd mountd CDs HPFS TI -# LocalWords: hpfs SYSV SCO intel iBCS Wyse WordPerfect tsx mit unixes sysv NR +# LocalWords: hpfs SYSV SCO Intel iBCS Wyse WordPerfect tsx mit unixes sysv NR # LocalWords: SMB WfW Cyclades async mux Logitech busmouse MouseSystem aka AST # LocalWords: PSMOUSE Compaq trackballs Travelmate Inport ATIXL ATI busmice ld # LocalWords: gpm config QIC DYNCONF FTAPE Stor Ftape ftape pcsndrv manpage NT # LocalWords: readprofile diskdrives org com masq EtherTalk tcp netrom sunacm # LocalWords: misc AIC aic pio nullmodems scc Portmaster eql GIS PhotoCDs MCDX -# LocalWords: mcdx gscd optcd sjcd ISP soundcard hdparm Workgroups Lan samba +# LocalWords: mcdx gscd optcd sjcd ISP hdparm Workgroups Lan samba # LocalWords: filesystems smbfs ATA ppp PCTech RZ www powerquest txt CMD ESDI -# LocalWords: chipset FB multicast MROUTE appletalk ifconfig IBMTR multiport +# LocalWords: chipset FB multicast MROUTE ifconfig IBMTR multiport # LocalWords: Multisession STALDRV EasyIO EC EasyConnection ISTALLION ONboard # LocalWords: Brumby pci TNC cis ohio faq usenet NETLINK dev hydra ca Tyne mem # LocalWords: carleton Deskstation DECstation SUNFD JENSEN Noname XXXM SLiRP -# LocalWords: pppd Zilog ZS soundcards SRM bootloader ez mainmenu rarp ipfwadm +# LocalWords: pppd Zilog ZS SRM bootloader ez mainmenu rarp ipfwadm # LocalWords: RTNETLINK mknod xos MTU lwared Macs mac netatalk macs cs Wolff # LocalWords: dartmouth flowerpt MultiMaster FlashPoint tudelft etherexpress # LocalWords: ICL EtherTeam ETH IDESCSI TXC SmartRAID SmartCache httpd sjc dlp # LocalWords: thesphere TwoServers BOOTP DHCP ncpfs BPQETHER BPQ chipsets MG -# LocalWords: bsd comp Sparcstation le SunOS ie Gracilis PackeTwin PT pt LU FX +# LocalWords: bsd comp SPARCstation le SunOS ie Gracilis PackeTwin PT pt LU FX # LocalWords: FX TEAC SoundBlaster CR CreativeLabs LCS mS ramdisk IDETAPE cmd # LocalWords: Vertos Genoa Funai hsfs NCP NetWare tgz APM apm ioctls UltraLite # LocalWords: TravelMate CDT LCD backlight VC RPC Mips DECStation AXP barlow diff -u --recursive --new-file v2.0.35/linux/Documentation/cdrom/aztcd linux/Documentation/cdrom/aztcd --- v2.0.35/linux/Documentation/cdrom/aztcd Mon Aug 5 00:13:50 1996 +++ linux/Documentation/cdrom/aztcd Sun Nov 15 10:32:42 1998 @@ -43,12 +43,12 @@ 1. NOTE This software has been successfully in alpha and beta test and is part of the standard kernel since kernel 1.1.8x since December 1994. It works with -with AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 and -CONRAD TXC (Nr.99 31 23 -series 04) and has proven to be stable with kernel -versions 1.0.9 to 1.3.72. But with any software there still may be bugs in it. -So if you encounter problems, you are invited to help us improve this software. -Please send me a detailed bug report (see chapter BUG REPORTS). You are also -invited in helping us to increase the number of drives, which are supported. +AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 and CONRAD TXC +(Nr.99 31 23 -series 04) and has proven to be stable with kernel versions 1.0.9 +to 1.3.72. But with any software there still may be bugs in it. So if you +encounter problems, you are invited to help us improve this software. Please +send me a detailed bug report (see chapter BUG REPORTS). You are also invited +in helping us to increase the number of drives, which are supported. Please read the README-files carefully and always keep a backup copy of your old kernel, in order to reboot if something goes wrong! diff -u --recursive --new-file v2.0.35/linux/Documentation/filesystems/vfat.txt linux/Documentation/filesystems/vfat.txt --- v2.0.35/linux/Documentation/filesystems/vfat.txt Mon Jul 13 13:46:24 1998 +++ linux/Documentation/filesystems/vfat.txt Sun Nov 15 10:32:42 1998 @@ -18,7 +18,7 @@ know how to deal with Unicode. There is also an option of doing UTF8 translations with the utf8 option. utf8 -- UTF8 is the filesystem safe version of Unicode that - is used by the console. It can be be enabled for the + is used by the console. It can be enabled for the filesystem with this option. If 'uni_xlate' gets set, UTF8 gets disabled. uni_xlate -- Translate unhandled Unicode characters to special diff -u --recursive --new-file v2.0.35/linux/Documentation/isdn/HiSax.cert linux/Documentation/isdn/HiSax.cert --- v2.0.35/linux/Documentation/isdn/HiSax.cert Wed Dec 31 16:00:00 1969 +++ linux/Documentation/isdn/HiSax.cert Sun Nov 15 10:32:42 1998 @@ -0,0 +1,76 @@ +-----BEGIN PGP SIGNED MESSAGE----- + +First: + + HiSax 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. + +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. +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. + +If you change the main files of the HiSax ISDN stack, the certification will +become invalid. Because in most countries it is illegal to connect +unapproved ISDN equipment to the public network, I have to guarantee that +changes in HiSax do not affect the certification. + +In order to make a valid certification apparent to the user, I have built in +some validation checks that are made during the make process. The HiSax main +files are protected by md5 checksums and the md5sum file is pgp signed by +myself: + +KeyID 1024/FF992F6D 1997/01/16 Karsten Keil +Key fingerprint = 92 6B F7 58 EE 86 28 C8 C4 1A E6 DC 39 89 F2 AA + +Only if the checksums are OK, and the signature of the file +"drivers/isdn/hisax/md5sums.asc" match, is the certification valid; a +message confirming this is then displayed during the hisax init process. + +The affected files are: + +drivers/isdn/hisax/isac.c +drivers/isdn/hisax/isdnl1.c +drivers/isdn/hisax/isdnl2.c +drivers/isdn/hisax/isdnl3.c +drivers/isdn/hisax/tei.c +drivers/isdn/hisax/callc.c +drivers/isdn/hisax/l3dss1.c +drivers/isdn/hisax/l3_1tr6.c +drivers/isdn/hisax/cert.c +drivers/isdn/hisax/elsa.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. + +Here are the certification registration numbers for ELSA Quickstep cards: +German D133361J CETECOM ICT Services GmbH 0682 +European D133362J CETECOM ICT Services GmbH 0682 + + +Karsten Keil +keil@isdn4linux.de + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.3i +Charset: noconv + +iQCVAwUBNj5OKDpxHvX/mS9tAQFHuQP/WeImlqCcDZ2d132yAvRBWFULlJoSf1P/ +c1lVTeaWvsSaY5Cu9hrKhXXhPzeEaitUbcUBPXdpzFWCA5CE902lnz7AhgRC+HF1 +0qiKgkZZyc/5HKasFymR1+IWSLw30GesP3Di/ZMR3NJi8SlY9PIjx7hnEMunGSRO +1ufPvfWWuu8= +=nGJk +-----END PGP SIGNATURE----- diff -u --recursive --new-file v2.0.35/linux/Documentation/isdn/README linux/Documentation/isdn/README --- v2.0.35/linux/Documentation/isdn/README Sun Nov 15 10:49:23 1998 +++ linux/Documentation/isdn/README Sun Nov 15 10:32:42 1998 @@ -41,11 +41,11 @@ In the following Text, the terms MSN and EAZ are used. MSN is the abbreviation for (M)ultiple(S)ubscriber(N)umber, and applies - to Euro(EDSS1)-type lines. Usually it is simply the phone-number. + to Euro(EDSS1)-type lines. Usually it is simply the phone number. EAZ is the abbreviation of (E)ndgeraete(A)uswahl(Z)iffer and applies to German 1TR6-type lines. This is a one-digit string, - simply appended to the base phone-number + simply appended to the base phone number The internal handling is nearly identical, so replace the appropriate term to that one, which applies to your local ISDN-environment. @@ -62,7 +62,7 @@ read: raw D-channel-messages (format: depends on driver). ioctl: depends on driver, i.e. for the ICN-driver, the base-address of the ports and the shared memory on the card can be set and read - also the boot-code an the protocol software can be loaded into + also the boot-code and the protocol software can be loaded into the card. O N L Y !!! for debugging (no locking against other devices): @@ -74,7 +74,7 @@ 128 tty-devices (64 cuix and 64 ttyIx) with integrated modem-emulator: The functionality is almost the same as that of a serial device - (the line-discs are handled by the kernel, which lets you run + (the line-discs are handled by the kernel), which lets you run SLIP, CSLIP and asynchronous PPP through the devices. We have tested Seyon, minicom, CSLIP (uri-dip) PPP and mgetty (compiled with NO_FAX), XCept. @@ -82,57 +82,57 @@ The modem-emulation supports the following: 1.3.1 Commands: - ATA Answer incoming call. - ATD Dial, the number may contain: + ATA Answer incoming call. + ATD Dial, the number may contain: [0-9] and [,#.*WPT-S] the latter are ignored until 'S'. The 'S' must precede the number, if the line is a SPV (German 1TR6). - ATE0 Echo off. - ATE1 Echo on (default). + ATE0 Echo off. + ATE1 Echo on (default). ATH Hang-up. - ATH1 Off hook (ignored). + ATH1 Off hook (ignored). ATH0 Hang-up. - ATI Return "ISDN for Linux...". + ATI Return "ISDN for Linux...". ATI0 " ATI1 " - ATI2 Report of last connection. + ATI2 Report of last connection. ATO On line (data mode). ATQ0 Enable result codes (default). ATQ1 Disable result codes (default). - ATSx=y Set register x to y. - ATSx? Show contents of register x. + ATSx=y Set register x to y. + ATSx? Show contents of register x. ATV0 Numeric responses. ATV1 English responses (default). - ATZ Load registers and EAZ/MSN from Profile. - AT&Bx Set Send-Packet-size to x (max. 4000) + ATZ Load registers and EAZ/MSN from Profile. + AT&Bx Set Send-Packet-size to x (max. 4000) The real packet-size may be limited by the - low-level-driver used. i.e.: the HiSax-Module- + low-level-driver used. e.g. the HiSax-Module- limit is 2000. You will get NO Error-Message, - if you set it to higher Values, because at the + if you set it to higher values, because at the time of giving this command the corresponding driver may not be selected (see "Automatic Assignment") however the size of outgoing packets will be limited correctly. - AT&D0 Ignore DTR - AT&D2 DTR-low-edge: Hang up and return to + AT&D0 Ignore DTR + AT&D2 DTR-low-edge: Hang up and return to command mode (default). AT&D3 Same as AT&D2 but also resets all registers. - AT&Ex Set the EAZ/MSN for this channel to x. - AT&F Reset all registers and profile to "factory-defaults" - AT&Sx Set window-size (x = 1..8) (not yet implemented) - AT&V Show all settings. + AT&Ex Set the EAZ/MSN for this channel to x. + AT&F Reset all registers and profile to "factory-defaults" + AT&Sx Set window-size (x = 1..8) (not yet implemented) + AT&V Show all settings. AT&W0 Write registers and EAZ/MSN to profile. See also iprofd (5.c in this README). - AT&X0 BTX-mode off (default) - AT&X1 BTX-mode on. (S13.1=1, S14=0, S16=7, S18=7, S19=0) + AT&X0 BTX-mode off (default) + AT&X1 BTX-mode on. (S13.1=1, S14=0, S16=7, S18=7, S19=0) For voice-mode commands refer to README.audio - 1.3.2 Escape sequence: + 1.3.2 Escape sequence: During a connection, the emulation reacts just like a normal modem to the escape sequence +++. - (The escape character - default '+' - can be set in the + (The escape character - default '+' - can be set in the register 2). The DELAY must at least be 1.5 seconds long and delay between the escape characters must not exceed 0.5 seconds. @@ -180,19 +180,19 @@ 1 = T.70 protocol (Only for BTX!) on Bit 2: 0 = Don't hangup on DTR low. 1 = Hangup on DTR low. - Bit 3: 0 = Standard response messages - 1 = Extended response messages + Bit 3: 0 = Standard response messages + 1 = Extended response messages Bit 4: 0 = CALLER NUMBER before every RING. 1 = CALLER NUMBER after first RING. - 14 0 Layer-2 protocol: - 0 = X75/LAPB with I-frames - 1 = X75/LAPB with UI-frames + 14 0 Layer-2 protocol: + 0 = X75/LAPB with I-frames + 1 = X75/LAPB with UI-frames 2 = X75/LAPB with BUI-frames 3 = HDLC 4 = Transparent (audio) 15 0 Layer-3 protocol: (at the moment always 0) 0 = transparent - 16 250 Send-Packet-size/16 + 16 250 Send-Packet-size/16 17 8 Window-size (not yet implemented) 18 4 Bit coded register, Service-Octet-1 to accept, or to be used on dialout: @@ -211,7 +211,7 @@ 19 0 Service-Octet-2 20 0 Bit coded register (readonly) Service-Octet-1 of last call. - Bit mapping is the same like register 18 + Bit mapping is the same as register 18 21 0 Bit coded register (readonly) Set on incoming call (during RING) to octet 3 of calling party number IE (Numbering plan) @@ -229,17 +229,17 @@ All inactive physical lines are listening to all EAZs for incoming calls and are NOT assigned to a specific tty or network interface. When an incoming call is detected, the driver looks first for a network - interfaces and then for an opened tty which: + interface and then for an opened tty which: 1. is configured for the same EAZ. 2. has the same protocol settings for the B-channel. 3. (only for network interfaces if the security flag is set) contains the caller number in its access list. 4. Either the channel is not bound exclusively to another Net-interface, or - it is bound AND the other checks apply to exact this Interface. + it is bound AND the other checks apply to exactly this interface. (For usage of the bind-features, refer to the isdnctrl-man-page) - Only when a matching interface or tty is found, the call is accepted + Only when a matching interface or tty is found is the call accepted and the "connection" between the low-level-layer and the link-level-layer is established and kept until the end of the connection. In all other cases no connection is established. Isdn4linux can be @@ -275,8 +275,8 @@ 4. Device-inodes - The major and minor-numbers and its names are described in - Documentation/devices.txt. The major-numbers are: + The major and minor numbers and their names are described in + Documentation/devices.txt. The major numbers are: 43 for the ISDN-tty's. 44 for the ISDN-callout-tty's. @@ -285,7 +285,7 @@ 5. Application a) For some card-types, firmware has to be loaded into the cards, before - proceeding with device-independant setup. See README. + proceeding with device-independent setup. See README. for how to do that. b) If you only intend to use ttys, you are nearly ready now. @@ -321,23 +321,42 @@ the charge-info during and after the connection): isdnctrl chargehup isdn0 on - i) Setup the interface with ifconfig as usual, and set a route to it. + i) Set the dial mode of the interface: + isdnctrl dialmode isdn0 auto + "off" means that you (or the system) cannot make any connection + (neither incoming or outgoing connections are possible). Use + this if you want to be sure that no connections will be made. + "auto" means that the interface is in auto-dial mode, and will + attempt to make a connection whenever a network data packet needs + the interface's link. Note that this can cause unexpected dialouts, + and lead to a high phone bill! Some daemons or other pc's that use + this interface can cause this. + Incoming connections are also possible. + "manual" is a dial mode created to prevent the unexpected dialouts. + In this mode, the interface will never make any connections on its + own. You must explicitly initiate a connection with "isdnctrl dial + isdn0". You _must_ also hangup the line explicitly as well! There + is NO timeout in this mode. Use "isdnctrl hangup isdn0" to end the + connection. + "manual" is the default. - j) (optional) If you run X11 and have Tcl/Tk-wish Version4.0, you can use + j) Setup the interface with ifconfig as usual, and set a route to it. + + k) (optional) If you run X11 and have Tcl/Tk-wish version 4.0, you can use the script tools/tcltk/isdnmon. You can add actions for line-status changes. See the comments at the beginning of the script for how to do that. There are other tty-based tools in the tools-subdirectory contributed by Michael Knigge (imon), Volker Götz (imontty) and Andreas Kool (isdnmon). - k) For initial testing, you can set the verbose-level to 2 (default: 0). + l) For initial testing, you can set the verbose-level to 2 (default: 0). Then all incoming calls are logged, even if they are not addressed to one of the configured net-interfaces: isdnctrl verbose 2 - Now you are ready! A ping to the set address should now result in a - dial-out (look at syslog kernel-messages). - The phone-numbers and EAZs can be assigned at any time with isdnctrl. + Now you are ready! A ping to the set address should now result in an + automatic dial-out (look at syslog kernel-messages). + The phone numbers and EAZs can be assigned at any time with isdnctrl. You can add as many interfaces as you like with addif following the directions above. Of course, there may be some limitations. But we have tested as many as 20 interfaces without any problem. However, if you @@ -365,7 +384,7 @@ "isdnctrl secure off" - Switch of secure operation (default). + Switch off secure operation (default). "isdnctrl ihup [on|off]" Switch the hang-up-timer for incoming calls on or off. @@ -400,7 +419,7 @@ Selects the type of packet-encapsulation. The encapsulation can be changed only while an interface is down. - At the moment th following Values are supported: + At the moment the following values are supported: rawip (Default) Selects raw-IP-encapsulation. This means, MAC-headers are stripped off. @@ -436,13 +455,13 @@ Forces hangup of an interface. "isdnctrl bind , [exclusive]" - If you are using more than one ISDN-Card, it is sometimes necessary to - dial out using a specific Card or even preserve a specific Channel for - Dialout of a specific net-interface. This can be done with the above + If you are using more than one ISDN card, it is sometimes necessary to + dial out using a specific card or even preserve a specific channel for + dialout of a specific net-interface. This can be done with the above command. Replace by whatever you assigned while loading the - module. The is counting from zero. the upper Limit - depends on the card used. At the Moment no card supports more than - 2 Channels, so the upper limit is one. + module. The is counted from zero. The upper limit + depends on the card used. At the moment no card supports more than + 2 channels, so the upper limit is one. "isdnctrl unbind " unbinds a previously bound interface. @@ -451,8 +470,8 @@ If switched on, isdn4linux replies a REJECT to incoming calls, it cannot match to any configured interface. If switched off, nothing happens in this case. - You normally should NOT enable this feature, if the ISDN-adaptor is not - the only device, connected to the S0-bus. Otherwise it could happen, that + You normally should NOT enable this feature, if the ISDN adapter is not + the only device connected to the S0-bus. Otherwise it could happen that isdn4linux rejects an incoming call, which belongs to another device on the bus. @@ -507,7 +526,7 @@ If other drivers will not be affected, I will include the changes in the next release. For developers only, there is a second mailing-list. Write to me - (fritz@wuemaus.franken.de), if you want to join that list. + (fritz@isdn4linux.de), if you want to join that list. Have fun! diff -u --recursive --new-file v2.0.35/linux/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax --- v2.0.35/linux/Documentation/isdn/README.HiSax Mon Aug 4 17:33:59 1997 +++ linux/Documentation/isdn/README.HiSax Sun Nov 15 10:32:43 1998 @@ -1,7 +1,7 @@ HiSax is a Linux hardware-level driver for passive ISDN cards with Siemens chipset (ISAC_S 2085/2086/2186, HSCX SAB 82525). It is based on the Teles driver from Jan den Ouden. -It is meant to be used with isdn4linux, an ISDN link-level module for Linux +It is meant to be used with isdn4linux, an ISDN link-level module for Linux written by Fritz Elfert. This program is free software; you can redistribute it and/or modify @@ -23,24 +23,50 @@ --------------- Teles 8.0/16.0/16.3 and compatible ones +Teles 16.3c Teles S0/PCMCIA -Creatix PnP S0 -AVM A1 (Fritz) +Teles PCI +Teles S0Box +Creatix S0Box +Creatix PnP S0 +Compaq ISDN S0 ISA card +AVM A1 (Fritz, Teledat 150) +AVM Fritz PCMCIA +AVM Fritz PnP +AVM Fritz PCI ELSA Microlink PCC-16, PCF, PCF-Pro, PCC-8 ELSA Quickstep 1000 +ELSA Quickstep 1000PCI +ELSA Quickstep 3000 (same settings as QS1000) +ELSA Quickstep 3000PCI ELSA PCMCIA ITK ix1-micro Rev.2 +Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version) +Eicon.Diehl Diva Piccola +ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D) +Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter) +PCBIT-DP (OEM version of ASUSCOM NETWORK INC. ISDNLink) +HFC-2BS0 based cards (TeleInt SA1) +Sedlbauer Speed Card (Speed Win, Teledat 100, PCI, Fax+) +Sedlbauer Speed Star/Speed Star2 (PCMCIA) +Sedlbauer ISDN-Controller PC/104 +USR Sportster internal TA (compatible Stollmann tina-pp V3) +ith Kommunikationstechnik GmbH MIC 16 ISA card +Traverse Technologie NETjet PCI S0 card +Dr. Neuhaus Niccy PnP/PCI Note: PCF, PCF-Pro: up to now, only the ISDN part is supported PCC-8: not tested yet Teles PCMCIA is EXPERIMENTAL + Teles 16.3c is EXPERIMENTAL + Teles PCI is EXPERIMENTAL + Teles S0Box is EXPERIMENTAL + Eicon.Diehl Diva U interface not tested If you know other passive cards with the Siemens chipset, please let me know. To use the PNP cards you need the isapnptools. You can combine any card, if there is no conflict between the ressources -(io, mem, irq), with one exception: The ELSA PCMCIA cannot work with an other -non PCMCIA ELSA card at the same time. You cannot select ELSA ISA and ELSA -PCMCIA support at the same time during kernel config. +(io, mem, irq). Configuring the driver @@ -51,7 +77,7 @@ with LILO or LOADLIN or, if built as a module, using insmod/modprobe with parameters. There is also some config needed before you compile the kernel and/or -modules. It is enclose in the normal "make [menu]config" target at the +modules. It is included in the normal "make [menu]config" target at the kernel. Don't forget it, especially to select the right D-channel protocol. Please note: All PnP cards need to be configured with isapnp and will work @@ -98,7 +124,7 @@ correct one during kernel config. Valid values are "1" for German 1TR6, "2" for EDSS1 (Euro ISDN) and "3" for leased lines (no D-Channel). -The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying +The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying the I/O addresses of the ISAC and HSCX chips, respectively. Card types: @@ -113,15 +139,36 @@ 6 ELSA PCC/PCF cards io or nothing for autodetect (the iobase is required only if you have more than one ELSA card in your PC) - 7 ELSA Quickstep 1000 irq, io (from isapnp setup) - 7 ELSA PCMCIA irq, io (set with card manager) + 7 ELSA Quickstep 1000 irq, io (from isapnp setup) 8 Teles 16.3 PCMCIA irq, io 9 ITK ix1-micro Rev.2 irq, io - -At the moment IRQ sharing is not possible. Please make sure that your IRQ -is free and enabled for ISA use. -Note: For using the ELSA PCMCIA you need the cardmanager under MSDOS for -enabling in the moment, then boot linux with loadlin. + 10 ELSA PCMCIA irq, io (set with card manager) + 11 Eicon.Diehl Diva ISA PnP irq, io + 11 Eicon.Diehl Diva PCI no parameter + 12 ASUS COM ISDNLink irq, io (from isapnp setup) + 13 HFC-2BS0 based cards irq, io + 14 Teles 16.3c PnP irq, io + 15 Sedlbauer Speed Card irq, io + 15 Sedlbauer PC/104 irq, io + 15 Sedlbauer Speed PCI no parameter + 16 USR Sportster internal irq, io + 17 MIC card irq, io + 18 ELSA Quickstep 1000PCI no parameter + 19 Compaq ISDN S0 ISA card irq, io0, io1, io (from isapnp setup io=IO2) + 20 NETjet PCI card no parameter + 21 Teles PCI no parameter + 22 Sedlbauer Speed Star (PCMCIA) irq, io (set with card manager) + 24 Dr. Neuhaus Niccy PnP irq, io0, io1 (from isapnp setup) + 24 Dr. Neuhaus Niccy PCI no parameter + 25 Teles S0Box irq, io (of the used lpt port) + 26 AVM A1 PCMCIA (Fritz!) irq, io (set with card manager) + 27 AVM PnP (Fritz!PnP) irq, io (from isapnp setup) + 27 AVM PCI (Fritz!PCI) no parameter + 28 Sedlbauer Speed Fax+ irq, io (from isapnp setup) + + +At the moment IRQ sharing is only possible with PCI cards. Please make sure +that your IRQ is free and enabled for ISA use. Examples for module loading @@ -138,7 +185,7 @@ 4. Any ELSA PCC/PCF card, Euro ISDN modprobe hisax type=6 protocol=2 -5. Teles 16.3 PnP, Euro ISDN, with isapnp configured +5. Teles 16.3 PnP, Euro ISDN, with isapnp configured isapnp config: (INT 0 (IRQ 10 (MODE +E))) (IO 0 (BASE 0x0580)) (IO 1 (BASE 0x0180)) @@ -179,28 +226,50 @@ Note: the ID string must start with an alphabetical character! Card types: - - type - 1 Teles 16.0 pa=irq pb=membase pc=iobase - 2 Teles 8.0 pa=irq pb=membase - 3 Teles 16.3 pa=irq pb=iobase + +type + 1 Teles 16.0 pa=irq pb=membase pc=iobase + 2 Teles 8.0 pa=irq pb=membase + 3 Teles 16.3 pa=irq pb=iobase 4 Creatix/Teles PNP ONLY WORKS AS A MODULE ! - 5 AVM A1 (Fritz) pa=irq pb=iobase + 5 AVM A1 (Fritz) pa=irq pb=iobase 6 ELSA PCC/PCF cards pa=iobase or nothing for autodetect - 7 ELSA Quickstep 1000 ONLY WORKS AS A MODULE ! - 7 ELSA PCMCIA irq, io (set with card manager) - 8 Teles S0 PCMCIA pa=irq pb=iobase + 7 ELSA Quickstep 1000 ONLY WORKS AS A MODULE ! + 8 Teles S0 PCMCIA pa=irq pb=iobase 9 ITK ix1-micro Rev.2 pa=irq pb=iobase + 10 ELSA PCMCIA pa=irq, pb=io (set with card manager) + 11 Eicon.Diehl Diva ISAPnP ONLY WORKS AS A MODULE ! + 11 Eicon.Diehl Diva PCI no parameter + 12 ASUS COM ISDNLink ONLY WORKS AS A MODULE ! + 13 HFC-2BS0 based cards pa=irq pb=io + 14 Teles 16.3c PnP ONLY WORKS AS A MODULE ! + 15 Sedlbauer Speed Card pa=irq pb=io (Speed Win only as module !) + 15 Sedlbauer PC/104 pa=irq pb=io + 15 Sedlbauer Speed PCI no parameter + 16 USR Sportster internal pa=irq pb=io + 17 MIC card pa=irq pb=io + 18 ELSA Quickstep 1000PCI no parameter + 19 Compaq ISDN S0 ISA card ONLY WORKS AS A MODULE ! + 20 NETjet PCI card no parameter + 21 Teles PCI no parameter + 22 Sedlbauer Speed Star (PCMCIA) pa=irq, pb=io (set with card manager) + 24 Dr. Neuhaus Niccy PnP ONLY WORKS AS A MODULE ! + 24 Dr. Neuhaus Niccy PCI no parameter + 25 Teles S0Box irq, io (of the used lpt port) + 26 AVM A1 PCMCIA (Fritz!) irq, io (set with card manager) + 27 AVM PnP (Fritz!PnP) ONLY WORKS AS A MODULE ! + 27 AVM PCI (Fritz!PCI) no parameter + 28 Sedlbauer Speed Fax+ ONLY WORKS AS A MODULE ! Running the driver ------------------ -When you insmod isdn.o and hisax.o (or with the in-kernel version, during +When you insmod isdn.o and hisax.o (or with the in-kernel version, during boot time), a few lines should appear in your syslog. Look for something like: Apr 13 21:01:59 kke01 kernel: HiSax: Driver for Siemens chip set ISDN cards -Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.1 +Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.9 Apr 13 21:01:59 kke01 kernel: HiSax: Revisions 1.14/1.9/1.10/1.25/1.8 Apr 13 21:01:59 kke01 kernel: HiSax: Total 1 card defined Apr 13 21:01:59 kke01 kernel: HiSax: Card 1 Protocol EDSS1 Id=HiSax1 (0) @@ -215,22 +284,21 @@ Apr 13 21:01:59 kke01 kernel: HiSax: 2 channels added This means that the card is ready for use. -Cabling problems or line-downs are not detected, and only ELSA cards can detect -the S0 power. +Cabling problems or line-downs are not detected, and only some ELSA cards can +detect the S0 power. Remember that, according to the new strategy for accessing low-level drivers from within isdn4linux, you should also define a driver ID while doing insmod: Simply append hisax_id= to the insmod command line. This string MUST NOT start with a digit or a small 'x'! -At this point you can run a 'cat /dev/isdnctrl0' and view debugging -messages. +At this point you can run a 'cat /dev/isdnctrl0' and view debugging messages. -At the moment, debugging messages are enabled with the telesctrl tool: +At the moment, debugging messages are enabled with the hisaxctrl tool: - telesctrl DebugCmd + hisaxctrl DebugCmd - default is HiSax, if you didn't specified one. + default is HiSax, if you didn't specify one. DebugCmd is 1 for generic debugging 11 for layer 1 development debugging @@ -241,54 +309,80 @@ With DebugCmd set to 1: - 1 Link-level <--> hardware-level communication - 2 Top state machine - 4 D-Channel Q.931 (call control messages) - 8 D-Channel Q.921 - 16 B-Channel X.75 - 32 D-Channel l2 - 64 B-Channel l2 - 128 D-Channel link state debugging - 256 B-Channel link state debugging - 512 TEI debug - 1024 LOCK debug in callc.c - 2048 More paranoid debug in callc.c (not for normal use) + 0x0001 Link-level <--> hardware-level communication + 0x0002 Top state machine + 0x0004 D-Channel Frames for isdnlog + 0x0008 D-Channel Q.921 + 0x0010 B-Channel X.75 + 0x0020 D-Channel l2 + 0x0040 B-Channel l2 + 0x0080 D-Channel link state debugging + 0x0100 B-Channel link state debugging + 0x0200 TEI debug + 0x0400 LOCK debug in callc.c + 0x0800 More paranoid debug in callc.c (not for normal use) + 0x1000 D-Channel l1 state debugging + 0x2000 B-Channel l1 state debugging With DebugCmd set to 11: - 1 Warnings (default: on) - 2 IRQ status - 4 ISAC - 8 ISAC FIFO - 16 HSCX - 32 HSCX FIFO (attention: full B-Channel output!) - 64 D-Channel LAPD frame types + 0x0001 Warnings (default: on) + 0x0002 IRQ status + 0x0004 ISAC + 0x0008 ISAC FIFO + 0x0010 HSCX + 0x0020 HSCX FIFO (attention: full B-Channel output!) + 0x0040 D-Channel LAPD frame types + 0x0080 IPAC debug + 0x0100 HFC receive debug + 0x0200 ISAC monitor debug + 0x0400 D-Channel frames for isdnlog (set with 1 0x4 too) + 0x0800 D-Channel message verbose With DebugCmd set to 13: 1 Warnings (default: on) - 2 l3 protocol discriptor errors + 2 l3 protocol descriptor errors 4 l3 state machine 8 charge info debugging (1TR6) -For example, 'telesctrl HiSax 1 0x3ff' enables full generic debugging. +For example, 'hisaxctrl HiSax 1 0x3ff' enables full generic debugging. + +Because of some obscure problems with some switch equipment, the delay +between the CONNECT message and sending the first data on the B-channel is now +configurable with + +hisaxctrl 2 + in ms Value between 50 and 800 ms is recommended. + +Downloading Firmware +-------------------- +At the moment, the Sedlbauer speed fax+ is the only card, which +needs to download firmware. +The firmware is downloaded with the hisaxctrl tool: + + hisaxctrl 9 + + default is HiSax, if you didn't specify one, +where is the filename of the firmware file. + +For example, 'hisaxctrl HiSax 9 ISAR.BIN' downloads the firmware for +ISAR based cards (like the Sedlbauer speed fax+). Warning ------- -HiSax is a work in progress and may crash your machine. It has not been -certified and therefore operation on your PTT's ISDN network is probably -illegal. - +HiSax is a work in progress and may crash your machine. +For certification look at HiSax.cert file. Limitations ----------- At this time, HiSax only works on Euro ISDN lines and German 1TR6 lines. +For leased lines see appendix. - -Bugs +Bugs ---- -If you find any, please let me know. +If you find any, please let me know. Thanks @@ -301,23 +395,208 @@ Andreas Kool, Pekka Sarnila, Sim Yskes, Johan Myrre'en, Klaus-Peter Nischke (ITK AG), Christof Petig, Werner Fehn (ELSA GmbH), Volker Schmidt + Edgar Toernig and Marcus Niemann for the Sedlbauer driver + Stephan von Krawczynski + Juergen Quade for the Leased Line part + Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE), for ELSA PCMCIA support + Enrik Berkhan (enrik@starfleet.inka.de) for S0BOX specific stuff + Ton van Rosmalen for Teles PCI and more people who are hunting bugs. (If I forgot somebody, please - send me a mail). + send me a mail). Firma ELSA GmbH - + Firma Eicon.Diehl GmbH + Firma Dynalink NL + Firma ASUSCOM NETWORK INC. Taiwan + Firma S.u.S.E + Firma ith Kommunikationstechnik GmbH + Firma Traverse Technologie Australia + Firma Medusa GmbH (www.medusa.de). + Firma Quant-X Austria for sponsoring a DEC Alpha board+CPU + Firma Cologne Chip Designs GmbH + My girl friend and partner in life Ute for her patience with me. Enjoy, -Karsten Keil -keil@temic-ech.spacenet.de +Karsten Keil +keil@isdn4linux.de Appendix: Teles PCMCIA driver ----------------------------- -See +See http://www.stud.uni-wuppertal.de/~ea0141/pcmcia.html for instructions. + +Appendix: Linux and ISDN-leased lines +------------------------------------- + +Original from Juergen Quade, new version KKe. + +Attention NEW VERSION, the old leased line syntax won't work !!! + +You can use HiSax to connect your Linux-Box via an ISDN leased line +to e.g. the Internet: + +1. Build a kernel which includes the HiSax driver either as a module + or as part of the kernel. + cd /usr/src/linux + make menuconfig + + make clean; make dep; make zImage; make modules; make modules_install +2. Install the new kernel + cp /usr/src/linux/arch/i386/boot/zImage /etc/kernel/linux.isdn + vi /etc/lilo.conf + + lilo +3. in case the hisax driver is a "fixed" part of the kernel, configure + the driver with lilo: + vi /etc/lilo.conf + + lilo + Your lilo.conf _might_ look like the following: + + # LILO configuration-file + # global section + # teles 16.0 on IRQ=5, MEM=0xd8000, PORT=0xd80 + append="hisax=1,3,5,0xd8000,0xd80,HiSax" + # teles 16.3 (non pnp) on IRQ=15, PORT=0xd80 + # append="hisax=3,3,5,0xd8000,0xd80,HiSax" + boot=/dev/sda + compact # faster, but won't work on all systems. + linear + read-only + prompt + timeout=100 + vga = normal # force sane state + # Linux bootable partition config begins + image = /etc/kernel/linux.isdn + root = /dev/sda1 + label = linux.isdn + # + image = /etc/kernel/linux-2.0.30 + root = /dev/sda1 + label = linux.secure + + In the line starting with "append" you have to adapt the parameters + according to your card (see above in this file) + +3. boot the new linux.isdn kernel +4. start the ISDN subsystem: + a) load - if necessary - the modules (depends, whether you compiled + the ISDN driver as module or not) + According to the type of card you have to specify the necessary + driver parameter (irq, io, mem, type, protocol). + For the leased line the protocol is "3". See the table above for + the parameters, which you have to specify depending on your card. + b) configure i4l + /sbin/isdnctrl addif isdn0 + # EAZ 1 -- B1 channel 2 --B2 channel + /sbin/isdnctrl eaz isdn0 1 + /sbin/isdnctrl secure isdn0 on + /sbin/isdnctrl huptimeout isdn0 0 + /sbin/isdnctrl l2_prot isdn0 hdlc + # Attention you must not set an outgoing number !!! This won't work !!! + # The incomming number is LEASED0 for the first card, LEASED1 for the + # second and so on. + /sbin/isdnctrl addphone isdn0 in LEASED0 + # Here is no need to bind the channel. + c) in case the remote partner is a CISCO: + /sbin/isdnctrl encap isdn0 cisco-h + d) configure the interface + /sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP} + e) set the routes + /sbin/route add -host ${REMOTE_IP} isdn0 + /sbin/route add default gw ${REMOTE_IP} + f) switch the card into leased mode for each used B-channel + /sbin/hisaxctrl HiSax 5 1 + +Remarks: +a) If you have a CISCO don't forget to switch off the KEEP ALIVE option! +b) Use state of the art isdn4k-utils + +Here an example script: +#!/bin/sh +# Start/Stop ISDN lesaed line connection + +I4L_AS_MODULE=yes +I4L_REMOTE_IS_CISCO=no +I4L_MODULE_PARAMS="type=16 io=0x268 irq=7 " +I4L_DEBUG=no +I4L_LEASED_128K=yes +LOCAL_IP=192.168.1.1 +REMOTE_IP=192.168.2.1 + +case "$1" in + start) + echo "Starting ISDN ..." + if [ ${I4L_AS_MODULE} = "yes" ]; then + echo "loading modules..." + /sbin/modprobe hisax ${I4L_MODULE_PARAMS} + fi + # configure interface + /sbin/isdnctrl addif isdn0 + /sbin/isdnctrl secure isdn0 on + if [ ${I4L_DEBUG} = "yes" ]; then + /sbin/isdnctrl verbose 7 + /sbin/hisaxctrl HiSax 1 0xffff + /sbin/hisaxctrl HiSax 11 0xff + cat /dev/isdnctrl >/tmp/lea.log & + fi + if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then + /sbin/isdnctrl encap isdn0 cisco-h + fi + /sbin/isdnctrl huptimeout isdn0 0 + # B-CHANNEL 1 + /sbin/isdnctrl eaz isdn0 1 + /sbin/isdnctrl l2_prot isdn0 hdlc + # 1. card + /sbin/isdnctrl addphone isdn0 in LEASED0 + if [ ${I4L_LEASED_128K} = "yes" ]; then + /sbin/isdnctrl addslave isdn0 isdn0s + /sbin/isdnctrl secure isdn0s on + /sbin/isdnctrl huptimeout isdn0s 0 + # B-CHANNEL 2 + /sbin/isdnctrl eaz isdn0s 2 + /sbin/isdnctrl l2_prot isdn0s hdlc + # 1. card + /sbin/isdnctrl addphone isdn0s in LEASED0 + if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then + /sbin/isdnctrl encap isdn0s cisco-h + fi + fi + /sbin/isdnctrl status isdn0 on + # configure tcp/ip + /sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP} + /sbin/route add -host ${REMOTE_IP} isdn0 + /sbin/route add default gw ${REMOTE_IP} + # switch to leased mode + # B-CHANNEL 1 + /sbin/hisaxctrl HiSax 5 1 + if [ ${I4L_LEASED_128K} = "yes" ]; then + # B-CHANNEL 2 + /sbin/hisaxctrl HiSax 5 2 + fi + ;; + stop) + /sbin/ifconfig isdn0 down + /sbin/isdnctrl delif isdn0 + if [ ${I4L_DEBUG} = "yes" ]; then + killall cat + fi + if [ ${I4L_AS_MODULE} = "yes" ]; then + /sbin/rmmod hisax + /sbin/rmmod isdn + /sbin/rmmod ppp + /sbin/rmmod slhc + fi + ;; + *) + echo "Usage: $0 {start|stop}" + exit 1 +esac +exit 0 + diff -u --recursive --new-file v2.0.35/linux/Documentation/isdn/README.audio linux/Documentation/isdn/README.audio --- v2.0.35/linux/Documentation/isdn/README.audio Mon Aug 4 17:33:59 1997 +++ linux/Documentation/isdn/README.audio Sun Nov 15 10:32:43 1998 @@ -1,4 +1,4 @@ -$Id: README.audio,v 1.5 1997/02/23 23:53:46 fritz Exp $ +$Id: README.audio,v 1.5.2.1 1998/08/22 16:39:52 armin Exp $ ISDN subsystem for Linux. Description of audio mode. @@ -50,10 +50,11 @@ the application. See below for data format AT+VSD=x,y Set silence-detection parameters. - NO EFFECT, supported for compatibility - only. Possible parameters: - x = 0 ... 31 - y = 0 ... 255 + Possible parameters: + x = 0 ... 31 sensitivity threshold level. + (default 0 , deactivated) + y = 0 ... 255 range of interval in units + of 0.1 second. (default 70) AT+VSD=? Report possible parameters. AT+VSD? Show current parameters. @@ -89,8 +90,8 @@ End of audio data. (i.e. caused by a hangup of the remote side) Emulator stops recording, responding with VCON. - Abort recording, (send by appl.) Emulator - stops recording, sends DLE,ETX. + Abort recording, (send by appl.) Emulator + stops recording, sends DLE,ETX. Escape sequence for DLE in data stream. 0 Touchtone "0" received. ... @@ -102,13 +103,14 @@ C Touchtone "C" received. D Touchtone "D" received. + q quiet. Silence detected after non-silence. + s silence. Silence detected from the + start of recording. + Currently unsupported DLE sequences: c FAX calling tone received. b busy tone received. - q quiet. Silence detected after non-silence. - s silence. Silence detected from the - start of recording. Audio playback. diff -u --recursive --new-file v2.0.35/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt --- v2.0.35/linux/Documentation/networking/arcnet.txt Sun Nov 15 10:49:25 1998 +++ linux/Documentation/networking/arcnet.txt Sun Nov 15 10:32:44 1998 @@ -329,11 +329,10 @@ ifconfig arc0 insight route add insight arc0 route add freedom arc0 /* I would use the subnet here (like I said - to to in "single protocol" above), - but the rest of the subnet - unfortunately lies across the PPP - link on freedom, which confuses - things. */ + in "single protocol" above), but the + rest of the subnet unfortunately + lies across the PPP link on freedom, + which confuses things. */ route add default gw freedom And freedom gets configured like so: diff -u --recursive --new-file v2.0.35/linux/Documentation/networking/rcpci45 linux/Documentation/networking/rcpci45 --- v2.0.35/linux/Documentation/networking/rcpci45 Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/rcpci45 Sun Nov 15 10:32:44 1998 @@ -0,0 +1,85 @@ + + +Application Information + +The included application, called "rcc" (for RedCreek Control), is an +example of a user-space application (i.e., not running within kernel +space). It issues ioctl commands to communicate with the PCI driver. +It is intended for reference only. It can currently report any of +the following information: + + - PCI driver information ("getinfo") + - card statistics ("getstats") + - card's ip address & netmask ("getipnmask") + - card's mac address ("getmac") + - current speed ("getspeed") + - firmware version string ("getfirmware") + - status of the link (up or down) ("getstatus") + +This program needs to run as root, to avoid encountering permission +problems. An alternative is to change the permission and ownership +so that it runs as a setuid root process (for example, "chown +root.root rcc; chmod u+s rcc"). + + +Quick PCI driver background + +The adapter has its own IP and mac addresses which you have to +assign using the RedCreek manager (assuming the adapter is +running 3.X firmware). Your linux box will not know anything +about the adapter's IP address -- ie, the adapter will show up +as a regular nic. You will assign the linux box IP address using +the "ifconfig" command, as mentioned below. + +To compile the driver, simply type "make". +This, of course, assumes that you have GNU compiler environment +already setup on a linux box. The .c and .h files were copied +to a dos filesystem (the floppy), so you may have to use "dos2unix" to +convert it back to a unix text file. Keep in mind that the driver +currently works with kernels 2.0.X only. Furthermore, it was only +tested with kernel 2.0.34. There is work being done on porting the +driver to the 2.1.X kernel, however, it's still incomplete. + +When the compile is done, you'll send up with three object files +related to the driver: rcmtl.o, rcpci45.o, and rc.o; rcmtl.o +is the RedCreek API message layer; rcpci45.o is the upper level +Linux driver which contains the interface to the kernel. Finally, +since the driver is compiled as a loadable module, rcmtl.o and +rcpci45.o are linked together into a third file, rc.o, which is the +actual driver. + +To load the driver: + +"insmod rc" + +If you are in console mode, you'll see a few messages send by the +driver. One of the messages will indicated how many adapters were +found; the messages are also stored in /var/log/messages, so they +can be viewed later. If you are running X, then you'll have to +view the messages by examining /var/log/messages. + +The adapter will show up as a regular nic. Thus, if you have only +one nic (the pci card) in your box, you would at this point configure +it with the following commands: + +mandatory: +"ifconfig eth0 " +"route add -net eth0" + +optional (if you want to be able to access other networks): +"route add default gw eth0" + +Done. Type "ifconfig" to see "eth0" and the packet count, as well +as the IP address, net mask, etc. + +To unload the driver, you first have to shutdown the interface: + +"ifconfig eth0 down" + +Then you unload the driver with "rmmod rc". + + +For technical support, please send email to Pete Popov at +ppopov@redcreek.com. Please have as complete of a description of +the problem as possible. diff -u --recursive --new-file v2.0.35/linux/Documentation/networking/shaper.txt linux/Documentation/networking/shaper.txt --- v2.0.35/linux/Documentation/networking/shaper.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/shaper.txt Sun Nov 15 10:32:44 1998 @@ -0,0 +1,49 @@ +Traffic Shaper For Linux + +This is the current ALPHA release of the traffic shaper for Linux. It works +within the following limits: + +o Minimum shaping speed is currently about 9600 baud (it can only +shape down to 1 byte per clock tick) + +o Maximum is about 256K, it will go above this but get a bit blocky. + +o If you ifconfig the master device that a shaper is attached to down +then your machine will follow. + +o The shaper must be a module. + + +Setup: + + A shaper device is configured using the shapeconfig program. +Typically you will do something like this + +shapecfg attach shaper0 eth1 +shapecfg speed shaper0 64000 +ifconfig shaper0 myhost netmask 255.255.255.240 broadcast 1.2.3.4.255 up +route add -net some.network netmask a.b.c.d dev shaper0 + +The shaper should have the same IP address as the device it is attached to +for normal use. + +Gotchas: + + The shaper shapes transmitted traffic. It's rather impossible to +shape received traffic except at the end (or a router) transmitting it. + + Gated/routed/rwhod/mrouted all see the shaper as an additional device +and will treat it as such unless patched. Note that for mrouted you can run +mrouted tunnels via a traffic shaper to control bandwidth usage. + + The shaper is device/route based. This makes it very easy to use +with any setup BUT less flexible. You may well want to combine this patch +with Mike McLagan 's patch to allow routes to be +specified by source/destination pairs. + + There is no "borrowing" or "sharing" scheme. This is a simple +traffic limiter. I'd like to implement Van Jacobson and Sally Floyd's CBQ +architecture into Linux one day (maybe in 2.1 sometime) and do this with +style. + +Alan diff -u --recursive --new-file v2.0.35/linux/Documentation/networking/tlan.README linux/Documentation/networking/tlan.README --- v2.0.35/linux/Documentation/networking/tlan.README Sun Nov 15 10:49:25 1998 +++ linux/Documentation/networking/tlan.README Sun Nov 15 10:32:44 1998 @@ -1,10 +1,11 @@ -TLAN driver for Linux, version 0.43 +TLAN driver for Linux, version 1.0 README -Note: I (James) am not maintaining this driver anymore, as I no longer - have the equipment to do so. So it is available to anyone who - wishes to take it over ;) If someone needs to reach me about - it, my new email address is james@sovereign.org. +Well, I'm back. The TLAN driver seems pretty stable, so I'm +declaring this cycle of development finished, and calling the +driver 1.0. I will, of course continue to work on improving +the driver, and work towards a 2.0 release. + I. Supported Devices. diff -u --recursive --new-file v2.0.35/linux/Documentation/paride.txt linux/Documentation/paride.txt --- v2.0.35/linux/Documentation/paride.txt Sun Nov 15 10:49:25 1998 +++ linux/Documentation/paride.txt Sun Nov 15 10:32:44 1998 @@ -2,13 +2,13 @@ Linux and parallel port IDE devices -PARIDE-2.0.35 (c) 1997-8 Grant Guenther +PARIDE-1.03s (c) 1997-8 Grant Guenther ************************************************************************* -Special notes for the 2.0.35 version: +Special notes for the 2.0 version: -(i) This is the paride from 2.1.107 retrofitted to work with 2.0.34. +(i) This is the paride from 2.1 retrofitted to work with 2.0. (ii) PARPORT is _not_ supported. If you obtain the PARPORT patches for 2.0 and try to use them, it might work. I have not tried @@ -20,6 +20,9 @@ (iv) I have not built or tested PARIDE with SMP support in 2.0.35, use it at your own risk. +(v) The pcd driver in 2.1 now contains support for playing audio + discs. This functionality is not available in the 2.0 version. + ************************************************************************* 1. Introduction @@ -61,8 +64,8 @@ SyQuest EZ-135, EZ-230 & SparQ drives Avatar Shark Imation Superdisk LS-120 - FreeCom Power CD - Hewlett-Packard 5GB tape drive + FreeCom Power CD + Hewlett-Packard 5GB and 8GB tape drives Hewlett-Packard 7100 and 7200 CD-RW drives as well as most of the clone and no-name products on the market. @@ -71,7 +74,7 @@ subsystem, is actually structured in three parts. There is a base paride module which provides a registry and some common methods for accessing the parallel ports. The second component is a set of -high-level drivers for each of the different type of supported device: +high-level drivers for each of the different types of supported devices: pd IDE disk pcd ATAPI CD-ROM @@ -184,7 +187,7 @@ is the parallel port base address, the 0 is the protocol registration number and 36 is the chain ID. -This (2.0.34) version of PARIDE does not support chained devices on the +This special version of PARIDE does not support chained devices on the same parallel port. 2.2 Loading and configuring PARIDE as modules @@ -193,7 +196,7 @@ if you use them as loadable kernel modules. Note: using these drivers with the "kerneld" automatic module loading -system is not recommended, and is not documented here. +system is not recommended for beginners, and is not documented here. To use PARIDE, you must begin by @@ -303,13 +306,56 @@ mkdosfs /dev/pf0 mount /dev/pf0 /mnt -2.4 Using the pg driver + +2.4 The pf driver + +The pf driver is intended for use with parallel port ATAPI disk +devices. The most common devices in this category are PD drives +and LS-120 drives. Traditionally, media for these devices are not +partitioned. Consequently, the pf driver does not support partitioned +media. This may be changed in a future version of the driver. + + +2.5 Using the pt driver + +The pt driver for parallel port ATAPI tape drives is a minimal driver. +It does not yet support many of the standard tape ioctl operations. +For best performance, a block size of 32KB should be used. You will +probably want to set the parallel port delay to 0, if you can. + + +2.6 Using the pg driver The pg driver can be used in conjunction with the cdrecord program -to create CD-ROMs. For more information, and the required patches -to cdrecord, please visit http://www.torque.net/parport/cdr.html . +to create CD-ROMs. Please get cdrecord version 1.6.1a3 or later +from ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ (you may have to look +in the alpha subdirectory). To record CD-R media your parallel port +should ideally be set to EPP mode, and the "port delay" should be +set to 0. With those settings it is possible to record at 2x speed +without any buffer underruns. If you cannot get the driver to work +in EPP mode, try to use "bidirectional" or "PS/2" mode and 1x speeds only. + 3. Troubleshooting + +The most common problems that people report with the PARIDE drivers +concern the parallel port CMOS settings. At this time, none of the +PARIDE protocol modules support ECP mode, or any ECP combination modes. +If you are able to do so, please set your parallel port into EPP mode +using your CMOS setup procedure. + +Some parallel ports cannot reliably transfer data at full speed. To +offset the errors, the PARIDE protocol modules introduce a "port +delay" between each access to the i/o ports. Each protocol sets +a default value for this delay. In most cases, the user can override +the default and set it to 0 - resulting in somewhat higher transfer +rates. In some rare cases (especially with older 486 systems) the +default delays are not long enough. if you experience corrupt data +transfers, or unexpected failures, you may wish to increase the +port delay. The delay can be programmed using the "driveN" parameters +to each of the high-level drivers. Please see the notes above, or +read the comments at the beginning of the driver source files in +linux/drivers/block/paride. While a lot of testing has gone into these drivers to make them work as smoothly as possible, problems will arise. If you do have problems, diff -u --recursive --new-file v2.0.35/linux/Documentation/specialix.txt linux/Documentation/specialix.txt --- v2.0.35/linux/Documentation/specialix.txt Tue Aug 12 11:17:41 1997 +++ linux/Documentation/specialix.txt Sun Nov 15 10:32:44 1998 @@ -17,7 +17,10 @@ written by Dmitry Gorodchanin. The specialix IO8+ card programming information was obtained from the CL-CD1865 Data Book, and Specialix document number 6200059: IO8+ Hardware - Functional Specification. + Functional Specification, augmented by document number 6200088: + Merak Hardware Functional Specification. (IO8+/PCI is also + called Merak) + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -44,10 +47,10 @@ kernel sources? And the manual of one of the boards in your computer? -Adresses and interrupts -======================= +Addresses and interrupts +======================== -Addres dip switch settings: +Address dip switch settings: The dip switch sets bits 2-9 of the IO address. 9 8 7 6 5 4 3 2 @@ -67,6 +70,9 @@ other computer runs just fine with the Specialix card at 0x100.... The card occupies 4 addresses, but actually only two are really used. +The PCI version doesn't have any dip switches. The BIOS assigns +an IO address. + The driver now still autoprobes at 0x100, 0x180, 0x250 and 0x260. If that causes trouble for you, please report that. I'll remove autoprobing then. @@ -75,6 +81,9 @@ change any jumpers to change the IRQ. Just use a command line argument (irq=xx) to the insmod program to set the interrupt. +The BIOS assigns the IRQ on the PCI version. You have no say in what +IRQ to use in that case. + If your specialix cards are not at the default locations, you can use the kernel command line argument "specialix=io0,irq0,io1,irq1...". Here "io0" is the io address for the first card, and "irq0" is the @@ -99,6 +108,32 @@ in your /etc/lilo.conf file if you use lilo. +The Specialix driver is slightly odd: It allows you to have the second +or third card detected without having a first card. This has +advantages and disadvantages. A slot that isn't filled by an ISA card, +might be filled if a PCI card is detected. Thus if you have an ISA +card at 0x250 and a PCI card, you would get: + +sx0: specialix IO8+ Board at 0x100 not found. +sx1: specialix IO8+ Board at 0x180 not found. +sx2: specialix IO8+ board detected at 0x250, IRQ 12, CD1865 Rev. B. +sx3: specialix IO8+ Board at 0x260 not found. +sx0: specialix IO8+ board detected at 0xd800, IRQ 9, CD1865 Rev. B. + +This would happen if you don't give any probe hints to the driver. +If you would specify: + + specialix=0x250,11 + +you'd get the following messages: + +sx0: specialix IO8+ board detected at 0x250, IRQ 11, CD1865 Rev. B. +sx1: specialix IO8+ board detected at 0xd800, IRQ 9, CD1865 Rev. B. + +ISA probing is aborted after the IO address you gave is exhausted, and +the PCI card is now detected as the second card. The ISA card is now +also forced to IRQ11.... + Baud rates ========== @@ -111,27 +146,33 @@ fact is a divided by two mode). This is not enough to reach the rated 115k2 on all ports at the same time. With this clock rate you can only do 37% of this rate. This means that at 115k2 on all ports you are -going to loose characters (The chip cannot handle that many incoming +going to lose characters (The chip cannot handle that many incoming bits at this clock rate.) (Yes, you read that correctly: there is a limit to the number of -=bits=- per second that the chip can handle.) If you near the "limit" you will first start to see a graceful degradation in that the chip cannot keep the transmitter busy at all times. However with a central clock this slow, you can also get it to -miss incoming characters. +miss incoming characters. The driver will print a warning message when +you are outside the official specs. The messages usually show up in +the file /var/log/messages . The specialix card cannot reliably do 115k2. If you use it, you have to do "extensive testing" (*) to verify if it actually works. When "mgetty" communicates with my modem at 115k2 it reports: got: +++[0d]ATQ0V1H0[0d][0d][8a]O[cb][0d][8a] - ^^^^ ^^^^ ^^^^ + ^^^^ ^^^^ ^^^^ The three characters that have the "^^^" under them have suffered a bit error in the highest bit. In conclusion: I've tested it, and found -that it simply DOESN"T work for me. I also suspect that this is also +that it simply DOESN'T work for me. I also suspect that this is also caused by the baud rate being just a little bit out of tune. +I upgraded the crystal to 66Mhz on one of my Specialix cards. Works +great! Contact me for details. (Voids warranty, requires a steady hand +and more such restrictions....) + (*) Cirrus logic CD1864 databook, page 40. @@ -248,7 +289,7 @@ Ports and devices ================= -Port 0 is the one furthest from the ISA connector. +Port 0 is the one furthest from the card-edge connector. Devices: @@ -265,10 +306,20 @@ done echo "" +If your system doesn't come with these devices preinstalled, bug your +linux-vendor about this. They have had ample time to get this +implemented by now. You cannot have more than 4 boards in one computer. The card only supports 4 different interrupts. If you really want this, contact me about this and I'll give you a few tips (requires soldering iron).... + +If you have enough PCI slots, you can probably use more than 4 PCI +versions of the card though.... + +The PCI version of the card cannot adhere to the mechanical part of +the PCI spec because the 8 serial connectors are simply too large. If +it doesn't fit in your computer, bring back the card. ------------------------------------------------------------------------ diff -u --recursive --new-file v2.0.35/linux/MAINTAINERS linux/MAINTAINERS --- v2.0.35/linux/MAINTAINERS Sun Nov 15 10:49:26 1998 +++ linux/MAINTAINERS Sun Nov 15 10:32:44 1998 @@ -237,8 +237,10 @@ S: Maintained IDE/ATAPI CDROM DRIVER -P: Erik Andersen -M: andersee@debian.org +P: Jens Axboe +M: axboe@image.dk +P: Chris Zwilling +M: chris@cloudnet.com L: linux-kernel@vger.rutgers.edu S: Maintained @@ -248,10 +250,18 @@ L: linux-kernel@vger.rutgers.edu S: Maintained -ISDN SUBSYSTEM +ISDN SUBSYSTEM [GENERAL] P: Fritz Elfert -M: fritz@wuemaus.franken.de +M: fritz@isdn4linux.de L: isdn4linux@hub-wue.franken.de +W: http://www.isdn4linux.de +S: Maintained + +ISDN SUBSYSTEM HISAX +P: Karsten Keil +M: keil@isdn4linux.de +L: isdn4linux@hub-wue.franken.de +W: http://www.isdn4linux.de S: Maintained MODULE SUPPORT [GENERAL], KERNELD diff -u --recursive --new-file v2.0.35/linux/Makefile linux/Makefile --- v2.0.35/linux/Makefile Sun Nov 15 10:49:26 1998 +++ linux/Makefile Sun Nov 15 10:32:44 1998 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 0 -SUBLEVEL = 35 +SUBLEVEL = 36 ARCH = i386 @@ -367,6 +367,9 @@ endif depend dep: dep-files $(MODVERFILE) + +checkconfig: + perl -w scripts/checkconfig.pl `find * -name '*.[hcS]' -print | sort` ifdef CONFIGURATION ..$(CONFIGURATION): diff -u --recursive --new-file v2.0.35/linux/Rules.make linux/Rules.make --- v2.0.35/linux/Rules.make Fri Sep 20 07:00:33 1996 +++ linux/Rules.make Sun Nov 15 10:32:44 1998 @@ -22,11 +22,13 @@ unexport O_OBJS unexport L_OBJS unexport M_OBJS +unexport MI_OBJS unexport ALL_MOBJS # objects that export symbol tables unexport OX_OBJS unexport LX_OBJS unexport MX_OBJS +unexport MIX_OBJS unexport SYMTAB_OBJS unexport MOD_LIST_NAME @@ -103,7 +105,7 @@ ifneq "$(strip $(ALL_MOBJS))" "" PDWN=$(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh) endif -modules: $(ALL_MOBJS) dummy +modules: $(ALL_MOBJS) $(MIX_OBJS) $(MI_OBJS) dummy ifdef MOD_SUB_DIRS set -e; for i in $(MOD_SUB_DIRS); do $(MAKE) -C $$i modules; done endif @@ -141,7 +143,7 @@ # Exporting objects are: all objects that define symbol tables # ifdef CONFIG_MODVERSIONS -SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) +SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) $(MIX_OBJS) ifneq "$(strip $(SYMTAB_OBJS))" "" MODINCL = $(TOPDIR)/include/linux/modules @@ -152,6 +154,8 @@ $(CC) $(CFLAGS) -E -D__GENKSYMS__ $< | /sbin/genksyms $(MODINCL) $(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver)): $(TOPDIR)/include/linux/autoconf.h + +.PHONY: $(TOPDIR)/include/linux/modversions.h $(TOPDIR)/include/linux/modversions.h: $(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver)) @echo updating $(TOPDIR)/include/linux/modversions.h diff -u --recursive --new-file v2.0.35/linux/arch/alpha/boot/bootp.c linux/arch/alpha/boot/bootp.c --- v2.0.35/linux/arch/alpha/boot/bootp.c Mon Jul 13 13:46:24 1998 +++ linux/arch/alpha/boot/bootp.c Sun Nov 15 10:32:45 1998 @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/alpha/kernel/lca.c linux/arch/alpha/kernel/lca.c --- v2.0.35/linux/arch/alpha/kernel/lca.c Mon Jul 13 13:46:25 1998 +++ linux/arch/alpha/kernel/lca.c Sun Nov 15 10:32:45 1998 @@ -455,7 +455,7 @@ case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break; case MCHK_K_DCPERR: reason = "d-cache parity error"; break; case MCHK_K_ICPERR: reason = "i-cache parity error"; break; - case MCHK_K_SIO_SERR: reason = "SIO SERR occurred on on PCI bus"; break; + case MCHK_K_SIO_SERR: reason = "SIO SERR occurred on PCI bus"; break; case MCHK_K_SIO_IOCHK: reason = "SIO IOCHK occurred on ISA bus"; break; case MCHK_K_DCSR: reason = "MCHK_K_DCSR"; break; case MCHK_K_UNKNOWN: diff -u --recursive --new-file v2.0.35/linux/arch/alpha/kernel/smc.c linux/arch/alpha/kernel/smc.c --- v2.0.35/linux/arch/alpha/kernel/smc.c Mon Jul 13 13:46:25 1998 +++ linux/arch/alpha/kernel/smc.c Sun Nov 15 10:32:45 1998 @@ -1967,7 +1967,7 @@ ** the configuration is also updated. If the device function ** is currently disabled, only the local shadow copy is ** updated and the actual device function will be updated -** if/when is is enabled. +** if/when it is enabled. ** **-- */ diff -u --recursive --new-file v2.0.35/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c --- v2.0.35/linux/arch/alpha/kernel/traps.c Mon Jul 13 13:46:25 1998 +++ linux/arch/alpha/kernel/traps.c Sun Nov 15 10:32:45 1998 @@ -422,7 +422,7 @@ { if (regs.r0 != 112 && regs.r0 < 300) printk("", regs.r0, a0, a1, a2); - return -1; + return -ENOSYS; } extern asmlinkage void entMM(void); diff -u --recursive --new-file v2.0.35/linux/arch/i386/boot/compressed/head.S linux/arch/i386/boot/compressed/head.S --- v2.0.35/linux/arch/i386/boot/compressed/head.S Sat Mar 9 03:31:42 1996 +++ linux/arch/i386/boot/compressed/head.S Sun Nov 15 10:32:45 1998 @@ -28,7 +28,6 @@ .text #define __ASSEMBLY__ -#include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/i386/boot/compressed/misc.c linux/arch/i386/boot/compressed/misc.c --- v2.0.35/linux/arch/i386/boot/compressed/misc.c Tue Dec 2 13:52:31 1997 +++ linux/arch/i386/boot/compressed/misc.c Sun Nov 15 10:32:45 1998 @@ -97,6 +97,9 @@ * This is set up by the setup-routine at boot-time */ #define EXT_MEM_K (*(unsigned short *)0x90002) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *) 0x901e0) +#endif #define DRIVE_INFO (*(struct drive_info *)0x90080) #define SCREEN_INFO (*(struct screen_info *)0x90000) #define RAMDISK_SIZE (*(unsigned short *)0x901F8) @@ -313,7 +316,11 @@ void setup_normal_output_buffer() { +#ifdef STANDARD_MEMORY_BIOS_CALL if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) error("Less than 2MB of memory.\n"); +#endif output_data = (char *)0x100000; /* Points to 1M */ } @@ -325,7 +332,11 @@ void setup_output_buffer_if_we_run_high(struct moveparams *mv) { high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE); +#ifdef STANDARD_MEMORY_BIOS_CALL if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3*1024)) error("Less than 4MB of memory.\n"); +#endif mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START; high_loaded = 1; free_mem_end_ptr = (long)high_buffer_start; diff -u --recursive --new-file v2.0.35/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S --- v2.0.35/linux/arch/i386/boot/setup.S Sat Mar 30 10:58:57 1996 +++ linux/arch/i386/boot/setup.S Sun Nov 15 10:32:45 1998 @@ -232,6 +232,29 @@ loader_ok: ! Get memory size (extended mem, kB) +#ifndef STANDARD_MEMORY_BIOS_CALL + push ebx + + xor ebx,ebx ! preload new memory slot with 0k + mov [0x1e0], ebx + + mov ax,#0xe801 + int 0x15 + jc oldstylemem + + and ebx, #0xffff ! clear sign extend + shl ebx, 6 ! and go from 64k to 1k chunks + mov [0x1e0],ebx ! store extended memory size + + and eax, #0xffff ! clear sign extend + add [0x1e0],eax ! and add lower memory into total size. + + ! and fall into the old memory detection code to populate the + ! compatability slot. + +oldstylemem: + pop ebx +#endif mov ah,#0x88 int 0x15 mov [2],ax diff -u --recursive --new-file v2.0.35/linux/arch/i386/boot/tools/build.c linux/arch/i386/boot/tools/build.c --- v2.0.35/linux/arch/i386/boot/tools/build.c Wed Jul 10 03:10:59 1996 +++ linux/arch/i386/boot/tools/build.c Sun Nov 15 10:32:46 1998 @@ -30,9 +30,10 @@ #include #include /* contains read/write */ #include -#include #include +#include #include + #define MINIX_HEADER 32 diff -u --recursive --new-file v2.0.35/linux/arch/i386/config.in linux/arch/i386/config.in --- v2.0.35/linux/arch/i386/config.in Sun May 12 21:17:23 1996 +++ linux/arch/i386/config.in Sun Nov 15 10:32:46 1998 @@ -43,6 +43,12 @@ 486 CONFIG_M486 \ Pentium CONFIG_M586 \ PPro CONFIG_M686" Pentium + +# Conditionally compile MTRR manipulation support +if [ "$CONFIG_M686" = "y" -o "$CONFIG_M586" = "y" ]; then + bool 'Handle buggy SMP BIOSes with bad MTRR setup' CONFIG_MTRR +fi + endmenu source drivers/block/Config.in diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile --- v2.0.35/linux/arch/i386/kernel/Makefile Tue Sep 16 14:33:59 1997 +++ linux/arch/i386/kernel/Makefile Sun Nov 15 10:32:46 1998 @@ -26,6 +26,10 @@ ifdef SMP +ifdef CONFIG_MTRR +O_OBJS += mtrr.o +endif + O_OBJS += smp.o head.o: head.S $(TOPDIR)/include/linux/tasks.h @@ -62,6 +66,6 @@ $(CPP) -D__SMP__ -traditional $< -o $@ clean: - rm -f trampoline hexify + rm -f trampoline trampoline.hex hexify include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- v2.0.35/linux/arch/i386/kernel/entry.S Sun Nov 15 10:49:27 1998 +++ linux/arch/i386/kernel/entry.S Sun Nov 15 10:32:46 1998 @@ -699,5 +699,12 @@ .long SYMBOL_NAME(sys_nanosleep) .long SYMBOL_NAME(sys_mremap) .long 0,0 - .long SYMBOL_NAME(sys_vm86) - .space (NR_syscalls-166)*4 + .long SYMBOL_NAME(sys_vm86) /* 166 */ + .long 0 /* 167 */ + .long 0 /* 168 STREAMS poll */ + .long 0 /* 169 */ + .long 0,0,0,0,0,0,0,0,0,0 /* 170 - 179 */ + .long 0,0,0,0,0,0,0,0 /* 180 - 187 */ + .long 0 /* 188 STREAMS getpmsg */ + .long 0 /* 189 STREAMS putpmsg */ + .space (NR_syscalls-189)*4 diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- v2.0.35/linux/arch/i386/kernel/head.S Sun Nov 15 10:49:27 1998 +++ linux/arch/i386/kernel/head.S Sun Nov 15 10:32:46 1998 @@ -216,15 +216,16 @@ orb $0x10,%bx # by setting bit 4 setCx86($0xc3,%bx) + getCx86($0xfe) # DIR0 : let's check this is a 6x86(L) + andb $0xf0,%ax # should be 3xh + cmpb $0x30,%ax # + jne n6x86 + getCx86($0xe8) # now we can get CCR4 orb $0x80,%ax # and set bit 7 (CPUIDEN) movb %ax,%bx # to enable CPUID execution setCx86($0xe8,%bx) - getCx86($0xfe) # DIR0 : let's check this is a 6x86(L) - andb $0xf0,%ax # should be 3xh - cmpb $0x30,%ax # - jne n6x86 getCx86($0xe9) # CCR5 : we reset the SLOP bit andb $0xfd,%ax # so that udelay calculation movb %ax,%bx # is correct on 6x86(L) CPUs diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- v2.0.35/linux/arch/i386/kernel/irq.c Mon Mar 17 14:58:59 1997 +++ linux/arch/i386/kernel/irq.c Sun Nov 15 10:32:46 1998 @@ -345,7 +345,29 @@ { struct irqaction * action = *(irq + irq_action); int do_random = 0; - + int c,intm,mask; +#ifdef IRQ_DEBUG + static int count; + if (smp_processor_id() != 0 && count++ < 1000) + printk("IRQ %d: done by CPU %d\n",irq,smp_processor_id()); +#endif + if (irq >= 8) { + c = cache_A1; + intm = inb(0xA1); + mask = 1 << (irq - 8); + } else { + c = cache_21; + intm = inb(0x21); + mask = 1 << irq; + } + if (!(c & mask) || !(intm & mask)) { +#ifdef IRQ_DEBUG + printk("IRQ %d (proc %d):cache_x1=0x%x,INT mask=0x%x\n", irq, smp_processor_id(),c,intm); +#endif + /* better to return because the interrupt may be asserted again, + the bad thing is that we may loose some interrupts */ + return; + } #ifdef __SMP__ if(smp_threads_ready && active_kernel_processor!=smp_processor_id()) panic("IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id()); diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/ksyms.c linux/arch/i386/kernel/ksyms.c --- v2.0.35/linux/arch/i386/kernel/ksyms.c Sun Nov 15 10:49:27 1998 +++ linux/arch/i386/kernel/ksyms.c Sun Nov 15 10:32:46 1998 @@ -6,6 +6,7 @@ #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -16,6 +17,7 @@ X(x86_capability), X(dump_thread), X(dump_fpu), + X(get_pt_regs_for_task), XNOVERS(__do_delay), XNOVERS(down_failed), XNOVERS(down_failed_interruptible), diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c --- v2.0.35/linux/arch/i386/kernel/mtrr.c Wed Dec 31 16:00:00 1969 +++ linux/arch/i386/kernel/mtrr.c Sun Nov 15 10:32:46 1998 @@ -0,0 +1,602 @@ +/* + * Read and write Memory Type Range Registers (MTRRs) + * + * These machine specific registers contain information about + * caching of memory regions on Intel processors. + * + * This code has been derived from pform_mod.c by M. Tisch"auser + * (email martin@ikcbarka.fzk.de). Special thanks to mingo for + * his hint. + * + * (c) 1997 M. Ohlenroth + * NO WARRANTY: use this code at your own risk! + * + * This code is released under the GNU public license version 2 or + * later. + * + * modified to have a /proc/mtrr interface by M. Fr"ohlich, Jan. 1998 + * + * the user Interface is partly taken form mtrr-patch-v1.5 + * Richard Gooch may be reached by email at rgooch@atnf.csiro.au + * The postal address is: + * Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MTRR_CAP 0x0fe /* MTRRcap register */ +#define MTRR_VARIABLE 0x200 /* variable length registers */ +#define MTRR_FIXED64K 0x250 /* fixed size registers 64k */ +#define MTRR_FIXED16K 0x258 /* fixed size registers 16K */ +#define MTRR_FIXED4K 0x268 /* fixed size registers 4K */ +#define MTRR_DEFTYPE 0x2ff /* MTRRdefType register */ + +/* + * data type for the MTRRcap register + */ +typedef struct { + __u64 VCNT : 8 __attribute__ ((packed)), + FIX : 1 __attribute__ ((packed)), + __reserved_2 : 1 __attribute__ ((packed)), + WC : 1 __attribute__ ((packed)), + __reserved_1 : 53 __attribute__ ((packed)); +} MTRRcap_t __attribute__ ((packed)); + + +/* + * data type for the MTRRdefType register + */ +typedef struct { + __u64 Type : 8 __attribute__ ((packed)), + __reserved_1 : 2 __attribute__ ((packed)), + FE : 1 __attribute__ ((packed)), + E : 1 __attribute__ ((packed)), + __reserved_2 : 52 __attribute__ ((packed)); +} MTRRdefType_t __attribute__ ((packed)); + +/* FIXME implement the entry struct */ +typedef struct MTRRfix64K_t { + __u64 raw; +} MTRRfix64K_t __attribute__ ((packed)); + +typedef struct MTRRfix16K_t { + __u64 raw; +} MTRRfix16K_t __attribute__ ((packed)); + +typedef struct MTRRfix4K_t { + __u64 raw; +} MTRRfix4K_t __attribute__ ((packed)); + +/* + * data type for a pair of variable MTRR registers + */ +typedef struct { + struct { + __u64 Type : 8 __attribute__ ((packed)), + __reserved_1 : 4 __attribute__ ((packed)), + PhysBase : 24 __attribute__ ((packed)), + __reserved_2 : 28 __attribute__ ((packed)); + } MTRRphysBase __attribute__ ((packed)); + + struct { + __u64 __reserved_3 : 11 __attribute__ ((packed)), + V : 1 __attribute__ ((packed)), + PhysMask : 24 __attribute__ ((packed)), + __reserved_4 : 28 __attribute__ ((packed)); + } MTRRphysMask __attribute__ ((packed)); +} MTRRvar_t __attribute__ ((packed)); + +#define RAW_ACCESS64(data) (*(unsigned long long *)(&(data))) + +/* + * MTRR configuration struct + */ +struct mtrr_cntl_t { + MTRRcap_t MTRRcap; /* MTRR capability register */ + MTRRdefType_t MTRRdefType; /* MTRR default type register */ + MTRRfix64K_t fixed64; /* fixed length entries (raw data) */ + MTRRfix16K_t fixed16[2]; + MTRRfix4K_t fixed4[8]; + MTRRvar_t variable[0]; /* variable type entries */ +}; + +static struct mtrr_cntl_t *mtrrcntl = NULL; + +/* + * Deletes the variable MTRR *MTRRvar + */ +static inline void MTRRvar_delete(MTRRvar_t *MTRRvar) +{ + RAW_ACCESS64(MTRRvar->MTRRphysBase) = 0; + RAW_ACCESS64(MTRRvar->MTRRphysMask) = 0; +} + + +/* + * Sets the variable MTRR *MTRRvar + */ +static inline void MTRRvar_set(MTRRvar_t *MTRRvar, unsigned int type, + unsigned long base, unsigned long size) +{ + unsigned long val; + base >>= 12; + size >>= 12; + + MTRRvar->MTRRphysBase.Type = type; + MTRRvar->MTRRphysBase.PhysBase = base; + + MTRRvar->MTRRphysMask.V = 1; + val = 1<<25; + while (0 == (val & size)) val |= (val>>1); + MTRRvar->MTRRphysMask.PhysMask = val; +} + + +/* + * returns 1 if the variable MTRR entry *MTRRvar is valid, 0 otherwise + */ +static inline int MTRRvar_is_valid(const MTRRvar_t *MTRRvar) +{ + return MTRRvar->MTRRphysMask.V; +} + +/* + * returns the type of the variable MTRR entry *MTRRvar + */ +static inline int MTRRvar_get_type(const MTRRvar_t *MTRRvar) +{ + return MTRRvar->MTRRphysBase.Type; +} + +/* + * returns the base of the variable MTRR entry *MTRRvar + */ +static inline unsigned long long MTRRvar_get_base(const MTRRvar_t *MTRRvar) +{ + return ((unsigned long long)MTRRvar->MTRRphysBase.PhysBase) << 12; +} + +/* + * returns the size of the variable MTRR entry *MTRRvar + */ +static inline unsigned long long MTRRvar_get_size(const MTRRvar_t *MTRRvar) +{ + if (MTRRvar->MTRRphysMask.PhysMask == 0) { + return 0; + } else { + unsigned long size = 1; + const unsigned long Mask = MTRRvar->MTRRphysMask.PhysMask; + while (0 == (Mask & size)) size <<= 1; + return ((unsigned long long)size) << 12; + } +} + +/* + * returns the eflags register + */ +static inline int read_eflags(void) +{ + int ret; + asm volatile ( + "pushfl\n\t" + "popl %%eax\n\t" + :"=a" (ret) + : + ); + return ret; +} + +/* + * writes the eflags register + */ +static inline void write_eflags(int flag) +{ + asm volatile ( + "pushl %%eax\n\t" + "popfl\n\t" + : + :"a" (flag) + ); +} + +/* + * returns 1 if the mtrr's are supported by the current processor, 0 otherwise + */ +static inline int mtrr_detect(void) { + unsigned long flags; + int eflags; + int val; + +#define MSR_MASK 0x20 +#define MTRR_MASK 0x1000 +#define CPUID_MASK 0x200000 + /* this function may be called before the cpu_data array has + been initialized */ + save_flags(flags); sti(); + eflags = read_eflags(); + write_eflags(eflags ^ CPUID_MASK); + if (!((eflags ^ read_eflags()) & CPUID_MASK)) { + write_eflags(eflags); + restore_flags(flags); + return 0; + } + write_eflags(eflags); + restore_flags(flags); + + /* get the cpuid level */ + asm volatile ( + "xorl %%eax,%%eax\n\t" + "cpuid" + :"=a"(val)::"ebx","ecx","edx" + ); + if (val < 1) return 0; + /* get the x86_capability value */ + asm volatile ( + "movl $1,%%eax\n\t" + "cpuid" + :"=d"(val)::"ebx","ecx","eax" + ); + if (!(val & MSR_MASK)) return 0; + if (!(val & MTRR_MASK)) return 0; + + return 1; +#undef MSR_MASK +#undef MTRR_MASK +#undef CPUID_MASK +} + + +/* + * reads the mtrr configuration of the actual processor and returns + * this configuration on sucess. returns NULL if an error occured or + * if mtrr's are not supported. + */ +static struct mtrr_cntl_t *read_mtrr_configuration (void) { + struct mtrr_cntl_t *mtrrcntl; + int i; + size_t size; + MTRRcap_t MTRRcap; + + if (!mtrr_detect()) { + printk("/proc/mtrr: MTRR's are NOT supported\n"); + return NULL; + } + + RAW_ACCESS64(MTRRcap) = rdmsr(MTRR_CAP); + +/* #define DUMP_MTRR */ +#ifdef DUMP_MTRR + { + /* Written for a bugreport to Gigabyte ... */ + inline void print_msr(int num) { + unsigned long long tmp = rdmsr(num); + printk("MSR #%#06x: 0x%08lx%08lx\n", + num , (unsigned long)(tmp >> 32), + (unsigned long)tmp); + } + + print_msr(MTRR_CAP); + /* all variable type */ + for (i=0;i < MTRRcap.VCNT;i++) { + print_msr(MTRR_VARIABLE+2*i); + print_msr(MTRR_VARIABLE+2*i+1); + } + /* all fixed type */ + print_msr(MTRR_FIXED64K); + print_msr(MTRR_FIXED16K); + print_msr(MTRR_FIXED16K+1); + for (i=0;i<8;i++) + print_msr(MTRR_FIXED4K+i); + print_msr(MTRR_DEFTYPE); + } +#endif + + size = sizeof(struct mtrr_cntl_t) + sizeof(MTRRvar_t)*MTRRcap.VCNT; + if (NULL == (mtrrcntl = kmalloc(size, GFP_KERNEL))) return NULL; + memset(mtrrcntl, 0, size); + + /* read MTRRcap register */ + mtrrcntl->MTRRcap = MTRRcap; + + /* read MTRRdefType register */ + RAW_ACCESS64(mtrrcntl->MTRRdefType) = rdmsr(MTRR_DEFTYPE); + + /* read fixed length entries */ + if (mtrrcntl->MTRRdefType.E && mtrrcntl->MTRRdefType.FE) { + mtrrcntl->fixed64.raw = rdmsr(MTRR_FIXED64K); + mtrrcntl->fixed16[0].raw = rdmsr(MTRR_FIXED16K); + mtrrcntl->fixed16[1].raw = rdmsr(MTRR_FIXED16K+1); + for (i=0;i<8;i++) + mtrrcntl->fixed4[i].raw = rdmsr(MTRR_FIXED4K+i); + } + + /* read variable length entries */ + if (mtrrcntl->MTRRdefType.E) { + const int vcnt = mtrrcntl->MTRRcap.VCNT; + for (i = 0 ; i < vcnt ; i++) { + RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase) = + rdmsr(MTRR_VARIABLE + 2*i); + RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask) = + rdmsr(MTRR_VARIABLE + 2*i + 1); + } + } + + return mtrrcntl; +} + +/* + * initializes the global mtrr configuration + */ +/*__init_function(void init_mtrr_config(void)) FIXME*/ +void init_mtrr_config(void) +{ + mtrrcntl = read_mtrr_configuration(); +} + + +/* write back and invalidate cache */ +static inline void wbinvd(void) +{ + asm volatile("wbinvd"); +} + +/* flush tlb's */ +static inline void flush__tlb(void) +{ + asm volatile ( + "movl %%cr3, %%eax\n\t" + "movl %%eax, %%cr3\n\t" + : + : + : "memory", "eax"); +} + +/* clear page global enable and return previous value */ +static inline unsigned long clear_pge(void) +{ + unsigned long ret; + asm volatile ( + "movl %%cr4, %%eax\n\t" + "movl %%eax, %%edx\n\t" + "andl $0x7f, %%edx\n\t" + "movl %%edx, %%cr4\n\t" + : "=a" (ret) + : + : "memory", "cc", "eax", "edx"); + return ret; +} + +/* restores page global enable bit */ +static inline void restore_pge(unsigned long cr4) +{ + asm volatile ( + "movl %0, %%cr4\n\t" + : + : "r" (cr4) + : "memory"); +} + +/* ... */ +static inline void disable_cache(void) +{ + asm volatile ( + "movl %%cr0, %%eax\n\t" + "orl $0x40000000, %%eax\n\t" + "movl %%eax, %%cr0\n\t" + : + : + :"memory", "cc", "eax"); +} + +/* ... */ +static inline void enable_cache(void) +{ + asm volatile ( + "movl %%cr0, %%eax\n\t" + "andl $0xbfffffff, %%eax\n\t" + "movl %%eax, %%cr0" + : + : + :"memory", "cc", "eax"); +} + +/* clear the MTRRdefType.E and MTRRdefType.FE flag to disable these MTRR's */ +static inline void disable_mtrr(void) +{ + MTRRdefType_t MTRRdefType; + + RAW_ACCESS64(MTRRdefType) = rdmsr(MTRR_DEFTYPE); + MTRRdefType.E = 0; + MTRRdefType.FE = 0; + wrmsr(MTRR_DEFTYPE, RAW_ACCESS64(MTRRdefType)); +} + +/* + * written from pseudocode from intel + * (PentiumPro Family Developers manual Volume 3, P 322) + * + */ +static inline unsigned long pre_mtrr_change(void) +{ + unsigned long cr4; + + cr4 = clear_pge(); + + wbinvd(); + + disable_cache(); + + wbinvd(); + + flush__tlb(); + + disable_mtrr(); + + return cr4; +} + +/* + * written from pseudocode from intel + * (PentiumPro Family Developers manual Volume 3, P 322) + */ +static inline void post_mtrr_change(MTRRdefType_t MTRRdefType,unsigned long cr4) +{ + wbinvd(); + + flush__tlb(); + + wrmsr(MTRR_DEFTYPE, RAW_ACCESS64(MTRRdefType)); + + enable_cache(); + + restore_pge(cr4); +} + +/* + * writes all fixed mtrr's + */ +static inline void set_mtrr_fixed(void) { + int i; + + wrmsr(MTRR_FIXED64K,mtrrcntl->fixed64.raw); + wrmsr(MTRR_FIXED16K+0,mtrrcntl->fixed16[0].raw); + wrmsr(MTRR_FIXED16K+1,mtrrcntl->fixed16[1].raw); + for (i=0;i<8;i++) + wrmsr(MTRR_FIXED4K+i,mtrrcntl->fixed4[i].raw); +} + +/* + * writes all variable mtrr's + */ +static inline void set_mtrr_variable(void) { + int i; + const int vcnt = mtrrcntl->MTRRcap.VCNT; + + for (i = 0 ; i < vcnt ; i++ ) { + wrmsr(MTRR_VARIABLE +2*i, + RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase)); + wrmsr(MTRR_VARIABLE +2*i+1, + RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask)); + } +} + + +/* + * compares the mtrr_cntl_t structure second with that set by + * the boot processor, + * returns 0 if equal, + * -1 if the structs are not initialized, + * 1 if they are different + * + * the *second struct is assumed to be local, it is not locked! + */ +static inline int compare_mtrr_configuration(struct mtrr_cntl_t *second) { + int i, result = 0; + + if (NULL == mtrrcntl) { result = -1; goto end; } + if (NULL == second) { result = -1; goto end; } + if (RAW_ACCESS64(mtrrcntl->MTRRcap) + != RAW_ACCESS64(second->MTRRcap)) { + result = 1; goto end; + } + + if (RAW_ACCESS64(mtrrcntl->MTRRdefType) + != RAW_ACCESS64(second->MTRRdefType)) { + result = 1; goto end; + } + + if (mtrrcntl->fixed64.raw != second->fixed64.raw) { + result = 1; goto end; + } + if (mtrrcntl->fixed16[0].raw != second->fixed16[0].raw) { + result = 1; goto end; + } + if (mtrrcntl->fixed16[1].raw != second->fixed16[1].raw) { + result = 1; goto end; + } + + for (i=0;i<8;i++) + if (mtrrcntl->fixed4[i].raw != second->fixed4[i].raw) { + result = 1; goto end; + } + { + const int vcnt = mtrrcntl->MTRRcap.VCNT; + for (i = 0; i < vcnt; i++) { + if (RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase) != + RAW_ACCESS64(second->variable[i].MTRRphysBase)) { + result = 1; goto end; + } + if (RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask) != + RAW_ACCESS64(second->variable[i].MTRRphysMask)) { + result = 1; goto end; + } + } + } + + end: + + return result; +} + +/* + * compares the mtrr configuration of the current processor with the + * main configuration and overwrites the mtrr's in the processor if they + * differ. (fixes a bug in the GA-686DX mainboard BIOS) + */ +void check_mtrr_config(void) { + unsigned long cr4; + unsigned long flags; + struct mtrr_cntl_t *this_cpu_setting; + int result; + + save_flags(flags); sti(); + + /* if global struct is not initialized return */ + if (mtrrcntl == NULL) { + restore_flags(flags); + return; + } + + /* disable MTRR feature if this_cpu_setting == NULL */ + /* read mtrr configuration of this cpu */ + this_cpu_setting = read_mtrr_configuration(); + if (this_cpu_setting == NULL) { + printk("/proc/mtrr: MTRR's are NOT supported by cpu %i.\n", + smp_processor_id()); + restore_flags(flags); + + return; + } + /* compare mtrr configuration */ + result = compare_mtrr_configuration(this_cpu_setting); + kfree(this_cpu_setting); + /* return if mtrr setting is correct */ + if (0 >= result) { + restore_flags(flags); + return; + } + + /* prepare cpu's for setting mtrr's */ + cr4 = pre_mtrr_change(); + + /* set all mtrr's */ + set_mtrr_fixed(); + set_mtrr_variable(); + + /* prepare cpu's for running */ + post_mtrr_change(mtrrcntl->MTRRdefType, cr4); + + restore_flags(flags); + + printk("\nBIOS bug workaround: MTRR configuration changed on cpu " + "%i.\n", smp_processor_id()); +} + diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.0.35/linux/arch/i386/kernel/setup.c Sun Nov 15 10:49:27 1998 +++ linux/arch/i386/kernel/setup.c Sun Nov 15 10:32:46 1998 @@ -42,15 +42,21 @@ char x86_model = 0; /* set by kernel/head.S */ char x86_mask = 0; /* set by kernel/head.S */ int x86_capability = 0; /* set by kernel/head.S */ +int x86_ext_capability = 0; /* newer CPUs have this */ int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */ int have_cpuid = 0; /* set if CPUID instruction works */ +int ext_cpuid = 0; /* if != 0, highest available CPUID value */ -char x86_vendor_id[13] = "unknown"; +char x86_vendor_id[13] = "GenuineIntel";/* default */ -unsigned char Cx86_step = 0; -static const char *Cx86_type[] = { - "unknown", "1.3", "1.4", "1.5", "1.6", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2" +static char *Cx86_step = "unknown"; /* stepping info for Cyrix CPUs */ + +static unsigned char Cx86_mult = 0; /* clock multiplier for Cyrix CPUs */ + +static const char *x86_clkmult[] = { + "unknown", "1", "1.5", "2", "2.5", "3", "3.5", "4", "4.5", "5", "5.5", + "6", "6.5", "7", "7.5", "8" }; char ignore_irq13 = 0; /* set if exception 16 works */ @@ -89,6 +95,9 @@ */ #define PARAM empty_zero_page #define EXT_MEM_K (*(unsigned short *) (PARAM+2)) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0)) +#endif #ifdef CONFIG_APM #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64)) #endif @@ -116,6 +125,9 @@ unsigned long * memory_start_p, unsigned long * memory_end_p) { unsigned long memory_start, memory_end; +#ifndef STANDARD_MEMORY_BIOS_CALL + unsigned long memory_alt_end; +#endif char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; static unsigned char smptrap=0; @@ -134,6 +146,15 @@ #endif aux_device_present = AUX_DEVICE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); +#ifndef STANDARD_MEMORY_BIOS_CALL + memory_alt_end = (1<<20) + (ALT_MEM_K<<10); + if (memory_alt_end > memory_end) { + printk("Memory: sized by int13 0e801h\n"); + memory_end = memory_alt_end; + } + else + printk("Memory: sized by int13 088h\n"); +#endif memory_end &= PAGE_MASK; #ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; @@ -144,6 +165,17 @@ if (memory_end > 16*1024*1024) memory_end = 16*1024*1024; #endif + + /* + * The 1Gig sanity checker. + */ + + if (memory_end > 980*1024*1024) + { + printk(KERN_WARNING "Warning only 980Mb will be used.\n"); + memory_end = 980 * 1024 * 1024; + } + if (!MOUNT_ROOT_RDONLY) root_mountflags &= ~MS_RDONLY; memory_start = (unsigned long) &_end; @@ -211,95 +243,107 @@ request_region(0xf0,0x10,"npu"); } -static const char * i486model(unsigned int nr) -{ - static const char *model[] = { - "0","DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB", - "10","11","12","13","Am5x86-WT","Am5x86-WB" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; -} - -static const char * i586model(unsigned int nr) +static const char * IDTmodel(void) +/* Right now IDT has a single CPU model in production: the C6. + * Adjust this when IDT/Centaur comes out with a new CPU model. + * Stepping information is correctly reported in x86_mask. + */ { static const char *model[] = { - "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83", - "Pentium MMX", NULL, NULL, "Mobile Pentium 75+", - "Mobile Pentium MMX" + "C6", "C6-3D" }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + return model[0]; } static const char * Cx86model(void) +/* We know our CPU is a Cyrix now (see bugs.h), so we can use the DIR0/DIR1 + * mechanism to figure out the model, bus clock multiplier and stepping. + * For the newest CPUs (GXm and MXi) we use the Extended CPUID function. + */ { - unsigned char nr6x86 = 0; - static const char *model[] = { - "unknown", "6x86", "6x86L", "6x86MX", "MII" - }; - switch (x86) { - case 5: - nr6x86 = ((x86_capability & (1 << 8)) ? 2 : 1); /* cx8 flag only on 6x86L */ - break; - case 6: - nr6x86 = 3; - break; - default: - nr6x86 = 0; + unsigned char nr6x86 = 0; + unsigned char cx_dir0 = 0; /* Model and bus clock multiplier */ + unsigned char cx_dir1 = 0; /* Stepping info */ + unsigned int flags; + static const char *model[] = { + "unknown", "Cx486", "5x86", "MediaGX", "6x86", "6x86L", "6x86MX", + "M II" + }; + + if (x86_model == -1) { /* is this an old Cx486 without DIR0/DIR1? */ + nr6x86 = 1; /* Cx486 */ + Cx86_mult = 0; /* unknown multiplier */ + } + else { + + /* Get DIR0, DIR1 since all other Cyrix CPUs have them */ + + save_flags(flags); + cli(); + cx_dir0 = getCx86(CX86_DIR0); /* we use the access macros */ + cx_dir1 = getCx86(CX86_DIR1); /* defined in processor.h */ + restore_flags(flags); + + /* Now cook; the recipe is by Channing Corn, from Cyrix. + * We do the same thing for each generation: we work out + * the model, multiplier and stepping. + */ + + if (cx_dir0 < 0x20) { + nr6x86 = 1; /* Cx486 */ + Cx86_mult = 0; /* unknown multiplier */ + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); } - - /* We must get the stepping number by reading DIR1 */ - outb(0xff, 0x22); x86_mask=inb(0x23); - - switch (x86_mask) { - case 0x03: - Cx86_step = 1; /* 6x86MX Rev 1.3 */ - break; - case 0x04: - Cx86_step = 2; /* 6x86MX Rev 1.4 */ - break; - case 0x05: - Cx86_step = 3; /* 6x86MX Rev 1.5 */ - break; - case 0x06: - Cx86_step = 4; /* 6x86MX Rev 1.6 */ - break; - case 0x14: - Cx86_step = 5; /* 6x86 Rev 2.4 */ - break; - case 0x15: - Cx86_step = 6; /* 6x86 Rev 2.5 */ - break; - case 0x16: - Cx86_step = 7; /* 6x86 Rev 2.6 */ - break; - case 0x17: - Cx86_step = 8; /* 6x86 Rev 2.7 or 3.7 */ - break; - case 0x22: - Cx86_step = 9; /* 6x86L Rev 4.2 */ - break; - default: - Cx86_step = 0; + + if ((cx_dir0 > 0x20) && (cx_dir0 < 0x30)) { + nr6x86 = 2; /* 5x86 */ + Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */ + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); } - return model[nr6x86]; -} - -static const char * i686model(unsigned int nr) -{ - static const char *model[] = { - "PPro A-step", "Pentium Pro" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + + if ((cx_dir0 >= 0x30) && (cx_dir0 < 0x38)) { + nr6x86 = ((x86_capability & (1 << 8)) ? 5 : 4); /* 6x86(L) */ + Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */ + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 3), cx_dir1 & 0x0f); + } + + if ((cx_dir0 >= 0x40) && (cx_dir0 < 0x50)) { + if (x86 == 4) { /* MediaGX */ + nr6x86 = 3; + Cx86_mult = ((cx_dir0 & 0x01) ? 5 : 7); /* either 3x or 4x */ + switch (cx_dir1 >> 4) { + case (0) : + case (1) : + sprintf(Cx86_step, "2.%d", cx_dir1 & 0x0f); + break; + case (2) : + sprintf(Cx86_step, "1.%d", cx_dir1 & 0x0f); + break; + default : + break; + } + } /* endif MediaGX */ + if (x86 == 5) { /* GXm */ + char GXm_mult[8] = {7,11,7,11,13,15,13,9}; /* 4 to 8 */ + ext_cpuid = 0x80000005; /* available */ + Cx86_mult = GXm_mult[cx_dir0 & 0x0f]; + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) - 1, cx_dir1 & 0x0f); + } /* endif GXm */ + } + + if ((cx_dir0 >= 0x50) && (cx_dir0 < 0x60)) { + nr6x86 = ((cx_dir1 > 7) ? 7 : 6); /* 6x86Mx or M II */ + Cx86_mult = (cx_dir0 & 0x07) + 2; /* 2 to 5 in 0.5 steps */ + if (((cx_dir1 & 0x0f) > 4) || ((cx_dir1 >> 4) == 2)) cx_dir1 += 0x10; + sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); + } + } + x86_mask = 1; /* we don't use it, but has to be set to something */ + return model[nr6x86]; } struct cpu_model_info { - int x86; + int cpu_x86; char *model_names[16]; }; @@ -308,10 +352,7 @@ { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4", "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }}, { 5, - { "K5/SSA5 (PR-75, PR-90, PR-100)", "K5 (PR-120, PR-133)", - "K5 (PR-166)", "K5 (PR-200)", NULL, NULL, - "K6 (166 - 266)", "K6 (166 - 300)", "K6-2 (200 - 450)", - "K6-3D-Plus (200 - 450)", NULL, NULL, NULL, NULL, NULL, NULL }}, + { "K5/SSA5 (PR-75, PR-90, PR-100)"}}, }; static const char * AMDmodel(void) @@ -319,41 +360,94 @@ const char *p=NULL; int i; - if (x86_model < 16) + if ((x86_model == 0) || (x86 == 4)) { for (i=0; i= 6)) { len += sprintf(buffer+len, - "stepping\t: %d\n", - CD(x86_mask)); + "stepping\t: %c\n", + x86_mask + 'A'); + } + else if (strncmp(x86_vendor_id, "Cy", 2) == 0) { + len += sprintf(buffer+len, + "stepping\t: %s, core/bus clock ratio: %sx\n", + Cx86_step, x86_clkmult[Cx86_mult]); } - else { /* we have a Cyrix */ + else { len += sprintf(buffer+len, - "stepping\t: %s\n", - Cx86_type[Cx86_step]); + "stepping\t: %d\n", + CD(x86_mask)); } else len += sprintf(buffer+len, @@ -431,6 +536,10 @@ if ( CD(x86_capability) & (1 << i) ) { len += sprintf(buffer+len, " %s", x86_cap_flags[i]); + } + else if ( CD(x86_ext_capability) & (1 << i) ) { + len += sprintf(buffer+len, " %s", + x86_ext_cap_flags[i]); } } len += sprintf(buffer+len, diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.0.35/linux/arch/i386/kernel/smp.c Mon Jul 13 13:46:25 1998 +++ linux/arch/i386/kernel/smp.c Sun Nov 15 10:32:46 1998 @@ -35,6 +35,9 @@ #include #include #include +#ifdef CONFIG_MTRR +#include +#endif /* * Why isn't this somewhere standard ?? @@ -552,6 +555,14 @@ l=apic_read(APIC_SPIV); l|=(1<<8); /* Enable */ apic_write(APIC_SPIV,l); + +#ifdef CONFIG_MTRR + /* + * checks the MTRR configuration of this application processor + */ + check_mtrr_config(); +#endif + sti(); /* * Get our bogomips. diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- v2.0.35/linux/arch/i386/kernel/traps.c Sun Nov 15 10:49:27 1998 +++ linux/arch/i386/kernel/traps.c Sun Nov 15 10:32:46 1998 @@ -192,9 +192,9 @@ DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) DO_ERROR(18, SIGSEGV, "reserved", reserved, current) -/* signal_return is directly after ret_from_sys_call in entry.S */ +/* divide_error is after ret_from_sys_call in entry.S */ asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); -asmlinkage void signal_return(void) __asm__("signal_return"); +asmlinkage void divide_error(void) __asm__("divide_error"); asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { @@ -210,7 +210,7 @@ */ if ((regs->cs & 3) != 3) { if (regs->eip >= (unsigned long)ret_from_sys_call && - regs->eip < (unsigned long)signal_return) { + regs->eip < (unsigned long)divide_error) { static int moancount = 0; if (moancount < 5) { printk(KERN_INFO "Ignoring GPF attempt from program \"%s\" (pid %d).\n", diff -u --recursive --new-file v2.0.35/linux/arch/i386/lib/delay.S linux/arch/i386/lib/delay.S --- v2.0.35/linux/arch/i386/lib/delay.S Tue Sep 16 14:55:07 1997 +++ linux/arch/i386/lib/delay.S Sun Nov 15 10:32:47 1998 @@ -6,8 +6,12 @@ */ ENTRY(__do_delay) -1: decl %eax - jns 1b + jmp 1f +.align 16 +1: jmp 2f +.align 16 +2: decl %eax + jns 2b ret diff -u --recursive --new-file v2.0.35/linux/arch/i386/math-emu/reg_u_sub.S linux/arch/i386/math-emu/reg_u_sub.S --- v2.0.35/linux/arch/i386/math-emu/reg_u_sub.S Thu Oct 5 06:30:43 1995 +++ linux/arch/i386/math-emu/reg_u_sub.S Sun Nov 15 10:32:47 1998 @@ -66,7 +66,7 @@ /* source 2 is always smaller than source 1 */ js L_bugged_1 - testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */ + testl $0x80000000,SIGH(%edi) /* The args are assumed to be normalized */ je L_bugged_2 testl $0x80000000,SIGH(%esi) diff -u --recursive --new-file v2.0.35/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c --- v2.0.35/linux/arch/i386/mm/init.c Tue Aug 12 13:06:54 1997 +++ linux/arch/i386/mm/init.c Sun Nov 15 10:32:47 1998 @@ -140,6 +140,17 @@ */ if (!smp_scan_config(639*0x400,0x400)) /* Scan the top 1K of base RAM */ smp_scan_config(0xF0000,0x10000); /* Scan the 64K of bios */ + /* + * If it is an SMP machine we should know now, unless the + * configuration is in an EISA/MCA bus machine with an + * extended bios data area. + * + * there is a real-mode segmented pointer pointing to the + * 4K EBDA area at 0x40E, calculate and scan it here: + */ + address = *(unsigned short *)phys_to_virt(0x40E); + address<<=4; + smp_scan_config(address, 0x1000); } /* * If it is an SMP machine we should know now, unless the configuration diff -u --recursive --new-file v2.0.35/linux/arch/m68k/amiga/cyberfb.c linux/arch/m68k/amiga/cyberfb.c --- v2.0.35/linux/arch/m68k/amiga/cyberfb.c Tue May 21 09:51:42 1996 +++ linux/arch/m68k/amiga/cyberfb.c Sun Nov 15 10:32:47 1998 @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/m68k/atari/atasound.c linux/arch/m68k/atari/atasound.c --- v2.0.35/linux/arch/m68k/atari/atasound.c Sun May 19 21:54:26 1996 +++ linux/arch/m68k/atari/atasound.c Sun Nov 15 10:32:47 1998 @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/m68k/atari/ksyms.c linux/arch/m68k/atari/ksyms.c --- v2.0.35/linux/arch/m68k/atari/ksyms.c Wed May 15 23:05:10 1996 +++ linux/arch/m68k/atari/ksyms.c Sun Nov 15 10:32:47 1998 @@ -1,4 +1,3 @@ -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/m68k/boot/amiga/bootstrap.c linux/arch/m68k/boot/amiga/bootstrap.c --- v2.0.35/linux/arch/m68k/boot/amiga/bootstrap.c Sun May 19 21:54:26 1996 +++ linux/arch/m68k/boot/amiga/bootstrap.c Sun Nov 15 10:32:48 1998 @@ -1,6 +1,6 @@ /* ** bootstrap.c -- This program loads the Linux/68k kernel into an Amiga -** and and launches it. +** and launches it. ** ** Copyright 1993,1994 by Hamish Macdonald, Greg Harp ** diff -u --recursive --new-file v2.0.35/linux/arch/m68k/kernel/sys_m68k.c linux/arch/m68k/kernel/sys_m68k.c --- v2.0.35/linux/arch/m68k/kernel/sys_m68k.c Wed May 15 23:05:10 1996 +++ linux/arch/m68k/kernel/sys_m68k.c Sun Nov 15 10:32:48 1998 @@ -6,7 +6,6 @@ * platform. */ -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/ppc/kernel/mk_defs.c linux/arch/ppc/kernel/mk_defs.c --- v2.0.35/linux/arch/ppc/kernel/mk_defs.c Mon Jul 8 01:27:42 1996 +++ linux/arch/ppc/kernel/mk_defs.c Sun Nov 15 10:32:48 1998 @@ -5,7 +5,6 @@ #define MK_DEFS #include -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c --- v2.0.35/linux/arch/ppc/kernel/pci.c Mon May 27 02:00:58 1996 +++ linux/arch/ppc/kernel/pci.c Sun Nov 15 10:32:48 1998 @@ -6,7 +6,6 @@ * be worked out to handle the differences. */ -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c --- v2.0.35/linux/arch/ppc/kernel/time.c Mon May 27 02:00:58 1996 +++ linux/arch/ppc/kernel/time.c Sun Nov 15 10:32:49 1998 @@ -26,7 +26,6 @@ #include #include -#include extern int isBeBox[]; diff -u --recursive --new-file v2.0.35/linux/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c --- v2.0.35/linux/arch/ppc/mm/fault.c Mon Jul 8 01:27:43 1996 +++ linux/arch/ppc/mm/fault.c Sun Nov 15 10:32:50 1998 @@ -5,7 +5,6 @@ * Ported to PPC by Gary Thomas */ -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/sparc/kernel/ksyms.c linux/arch/sparc/kernel/ksyms.c --- v2.0.35/linux/arch/sparc/kernel/ksyms.c Sun Mar 3 22:49:55 1996 +++ linux/arch/sparc/kernel/ksyms.c Sun Nov 15 10:32:50 1998 @@ -4,7 +4,6 @@ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ -#include #include #include diff -u --recursive --new-file v2.0.35/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c --- v2.0.35/linux/arch/sparc/kernel/sys_sunos.c Thu Apr 25 03:22:05 1996 +++ linux/arch/sparc/kernel/sys_sunos.c Sun Nov 15 10:32:50 1998 @@ -26,7 +26,6 @@ * to do the inverse mapping. */ -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v2.0.35/linux/drivers/block/floppy.c Mon Jul 13 13:46:26 1998 +++ linux/drivers/block/floppy.c Sun Nov 15 10:32:51 1998 @@ -328,7 +328,7 @@ 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/ {{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0, - 0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/ + 0, { 2, 5, 6,23,10,20,12, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/ {{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/ @@ -1682,12 +1682,14 @@ if(do_print) print_result("unexpected interrupt", inr); if (inr == 0){ + int max_sensei = 4; do { output_byte(FD_SENSEI); inr = result(); if(do_print) print_result("sensei", inr); - } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2); + max_sensei--; + } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2 && max_sensei); } if (handler) { if(intr_count >= 2) { @@ -3989,6 +3991,8 @@ #endif if (floppy_grab_irq_and_dma()){ + del_timer(&fd_timeout); + blk_dev[MAJOR_NR].request_fn = NULL; unregister_blkdev(MAJOR_NR,"fd"); return -EBUSY; } @@ -4042,6 +4046,17 @@ initialising=0; if (have_no_fdc) { DPRINT("no floppy controllers found\n"); + request_tq.routine = (void *)(void *) empty; + /* + * When we return we may be unloaded. This little + * trick forces the immediate_bh handler to have run + * before we unload it, lest we cause bad things. + */ + mark_bh(IMMEDIATE_BH); + schedule(); + if (usage_count) + floppy_release_irq_and_dma(); + blk_dev[MAJOR_NR].request_fn = NULL; unregister_blkdev(MAJOR_NR,"fd"); } return have_no_fdc; diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v2.0.35/linux/drivers/block/ide-cd.c Sun Nov 15 10:49:31 1998 +++ linux/drivers/block/ide-cd.c Sun Nov 15 10:32:51 1998 @@ -6,6 +6,7 @@ * * Copyright (C) 1994, 1995, 1996 scott snyder * Copyright (C) 1996, 1997 Erik Andersen + * Copyright (C) 1998 Jens Axboe and Chris Zwilling * * May be copied or modified under the terms of the GNU General Public License * see linux/COPYING for more information. @@ -126,6 +127,10 @@ * Added identifier so new Sanyo CD-changer works * Better detection if door locking isn't supported * 3.21 Jun 16,1997 -- Add work-around for GCD-R580B + * + * 3.22 Nov 13, 1998 -- New ide-cd maintainers: + * Jens Axboe + * Chris Zwilling * * NOTE: Direct audio reads will only work on some types of drive. * So far, i've received reports of success for Sony and Toshiba drives. diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c --- v2.0.35/linux/drivers/block/ide-floppy.c Sun Nov 15 10:49:31 1998 +++ linux/drivers/block/ide-floppy.c Sun Nov 15 10:32:51 1998 @@ -28,6 +28,7 @@ * Issue START cmd only if TEST_UNIT_READY fails * Add CDROMEJECT ioctl * Clean up error messages a bit + * Ver 0.72 Jul 8 98 Limit max sectors only on IOMEGA ZIP 23.D */ #include @@ -1376,7 +1377,8 @@ if (gcw.drq_type == 1) set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags); if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0 && - strcmp(drive->id->fw_rev, "21.D") == 0) + ((strcmp(drive->id->fw_rev, "21.D") == 0) || + (strcmp(drive->id->fw_rev, "23.D") == 0))) floppy->max_sectors = 64; else floppy->max_sectors = IDEFLOPPY_MAX_SECTORS; diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c --- v2.0.35/linux/drivers/block/ide-tape.c Sun Nov 15 10:49:31 1998 +++ linux/drivers/block/ide-tape.c Sun Nov 15 10:32:51 1998 @@ -42,7 +42,7 @@ * flag, can be configured by issuing an ioctl to the block device interface, * as any other ide device. * - * Our own ide-tape ioctl's can can be issued to either the block device or + * Our own ide-tape ioctl's can be issued to either the block device or * the character device interface. * * Maximal throughput with minimal bus load will usually be achieved in the @@ -261,7 +261,7 @@ * loop which checks if the pipeline is empty, and if it is, we * increase the maximum number of stages as necessary until we * reach the optimum value which just manages to keep the tape - * busy with with minimum allocated memory or until we reach + * busy with minimum allocated memory or until we reach * IDETAPE_MAX_PIPELINE_STAGES. * * Concerning (2): diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.0.35/linux/drivers/block/ide.c Sun Nov 15 10:49:31 1998 +++ linux/drivers/block/ide.c Sun Nov 15 10:32:51 1998 @@ -980,7 +980,7 @@ #if FANCY_STATUS_DUMPS if (drive->media == ide_disk) { printk(" { "); - if (err & BBD_ERR) printk("BadSector "); + if (err & ICRC_ERR) printk((err & ABRT_ERR) ? "BadCRC " : "BadSector "); if (err & ECC_ERR) printk("UncorrectableError "); if (err & ID_ERR) printk("SectorIdNotFound "); if (err & ABRT_ERR) printk("DriveStatusError "); @@ -1908,9 +1908,9 @@ * This function issues a special IDE device request * onto the request queue. * - * If action is ide_wait, then then rq is queued at the end of - * the request queue, and the function sleeps until it has been - * processed. This is for use when invoked from an ioctl handler. + * If action is ide_wait, then the rq is queued at the end of the + * request queue, and the function sleeps until it has been processed. + * This is for use when invoked from an ioctl handler. * * If action is ide_preempt, then the rq is queued at the head of * the request queue, displacing the currently-being-processed diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v2.0.35/linux/drivers/block/ide.h Sun Nov 15 10:49:31 1998 +++ linux/drivers/block/ide.h Sun Nov 15 10:32:52 1998 @@ -605,9 +605,9 @@ * This function issues a special IDE device request * onto the request queue. * - * If action is ide_wait, then then rq is queued at the end of - * the request queue, and the function sleeps until it has been - * processed. This is for use when invoked from an ioctl handler. + * If action is ide_wait, then the rq is queued at the end of the + * request queue, and the function sleeps until it has been processed. + * This is for use when invoked from an ioctl handler. * * If action is ide_preempt, then the rq is queued at the head of * the request queue, displacing the currently-being-processed diff -u --recursive --new-file v2.0.35/linux/drivers/block/loop.c linux/drivers/block/loop.c --- v2.0.35/linux/drivers/block/loop.c Tue Aug 12 13:06:54 1997 +++ linux/drivers/block/loop.c Sun Nov 15 10:32:52 1998 @@ -365,7 +365,7 @@ case LO_CRYPT_NONE: break; case LO_CRYPT_XOR: - if (info.lo_encrypt_key_size < 0) + if (info.lo_encrypt_key_size <= 0) return -EINVAL; break; #ifdef DES_AVAILABLE diff -u --recursive --new-file v2.0.35/linux/drivers/block/md.c linux/drivers/block/md.c --- v2.0.35/linux/drivers/block/md.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/md.c Sun Nov 15 10:32:52 1998 @@ -543,7 +543,6 @@ RO_IOCTLS(inode->i_rdev,arg); default: - printk ("Unknown md_ioctl %d\n", cmd); return -EINVAL; } diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/Makefile linux/drivers/block/paride/Makefile --- v2.0.35/linux/drivers/block/paride/Makefile Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/Makefile Sun Nov 15 10:32:52 1998 @@ -12,6 +12,7 @@ MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) +MOD_LIST_NAME := PARIDE_MODULES L_TARGET := paride.a MX_OBJS := LX_OBJS := @@ -30,7 +31,7 @@ LX_OBJS += pd.o else ifeq ($(CONFIG_PARIDE_PD),m) - MX_OBJS += pd.o + M_OBJS += pd.o endif endif @@ -38,7 +39,7 @@ LX_OBJS += pcd.o else ifeq ($(CONFIG_PARIDE_PCD),m) - MX_OBJS += pcd.o + M_OBJS += pcd.o endif endif @@ -46,7 +47,7 @@ LX_OBJS += pf.o else ifeq ($(CONFIG_PARIDE_PF),m) - MX_OBJS += pf.o + M_OBJS += pf.o endif endif @@ -54,7 +55,7 @@ LX_OBJS += pt.o else ifeq ($(CONFIG_PARIDE_PT),m) - MX_OBJS += pt.o + M_OBJS += pt.o endif endif @@ -62,7 +63,7 @@ LX_OBJS += pg.o else ifeq ($(CONFIG_PARIDE_PG),m) - MX_OBJS += pg.o + M_OBJS += pg.o endif endif @@ -70,7 +71,7 @@ LX_OBJS += aten.o else ifeq ($(CONFIG_PARIDE_ATEN),m) - MX_OBJS += aten.o + M_OBJS += aten.o endif endif @@ -78,7 +79,7 @@ LX_OBJS += bpck.o else ifeq ($(CONFIG_PARIDE_BPCK),m) - MX_OBJS += bpck.o + M_OBJS += bpck.o endif endif @@ -86,7 +87,7 @@ LX_OBJS += comm.o else ifeq ($(CONFIG_PARIDE_COMM),m) - MX_OBJS += comm.o + M_OBJS += comm.o endif endif @@ -94,7 +95,7 @@ LX_OBJS += dstr.o else ifeq ($(CONFIG_PARIDE_DSTR),m) - MX_OBJS += dstr.o + M_OBJS += dstr.o endif endif @@ -102,7 +103,7 @@ LX_OBJS += kbic.o else ifeq ($(CONFIG_PARIDE_KBIC),m) - MX_OBJS += kbic.o + M_OBJS += kbic.o endif endif @@ -110,7 +111,7 @@ LX_OBJS += epat.o else ifeq ($(CONFIG_PARIDE_EPAT),m) - MX_OBJS += epat.o + M_OBJS += epat.o endif endif @@ -118,7 +119,7 @@ LX_OBJS += epia.o else ifeq ($(CONFIG_PARIDE_EPIA),m) - MX_OBJS += epia.o + M_OBJS += epia.o endif endif @@ -126,7 +127,7 @@ LX_OBJS += fit2.o else ifeq ($(CONFIG_PARIDE_FIT2),m) - MX_OBJS += fit2.o + M_OBJS += fit2.o endif endif @@ -134,7 +135,7 @@ LX_OBJS += fit3.o else ifeq ($(CONFIG_PARIDE_FIT3),m) - MX_OBJS += fit3.o + M_OBJS += fit3.o endif endif @@ -142,7 +143,7 @@ LX_OBJS += frpw.o else ifeq ($(CONFIG_PARIDE_FRPW),m) - MX_OBJS += frpw.o + M_OBJS += frpw.o endif endif @@ -150,7 +151,7 @@ LX_OBJS += on20.o else ifeq ($(CONFIG_PARIDE_ON20),m) - MX_OBJS += on20.o + M_OBJS += on20.o endif endif @@ -158,7 +159,7 @@ LX_OBJS += on26.o else ifeq ($(CONFIG_PARIDE_ON26),m) - MX_OBJS += on26.o + M_OBJS += on26.o endif endif @@ -166,7 +167,7 @@ LX_OBJS += ktti.o else ifeq ($(CONFIG_PARIDE_KTTI),m) - MX_OBJS += ktti.o + M_OBJS += ktti.o endif endif diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/bpck.c linux/drivers/block/paride/bpck.c --- v2.0.35/linux/drivers/block/paride/bpck.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/bpck.c Sun Nov 15 10:32:52 1998 @@ -10,10 +10,11 @@ /* Changes: 1.01 GRG 1998.05.05 init_proto, release_proto, pi->delay + 1.02 GRG 1998.08.15 default pi->delay returned to 4 */ -#define BPCK_VERSION "1.01" +#define BPCK_VERSION "1.02" #include #include @@ -450,7 +451,7 @@ { MOD_DEC_USE_COUNT; } -struct pi_protocol bpck = { "bpck",0,5,2,1,256, +struct pi_protocol bpck = { "bpck",0,5,2,4,256, bpck_write_regr, bpck_read_regr, bpck_write_block, diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/frpw.c linux/drivers/block/paride/frpw.c --- v2.0.35/linux/drivers/block/paride/frpw.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/frpw.c Sun Nov 15 10:32:52 1998 @@ -12,10 +12,11 @@ 1.01 GRG 1998.05.06 init_proto, release_proto fix chip detect added EPP-16 and EPP-32 + 1.02 GRG 1998.09.23 added hard reset to initialisation process */ -#define FRPW_VERSION "1.01" +#define FRPW_VERSION "1.02" #include #include @@ -183,6 +184,9 @@ /* returns chip_type: 0 = Xilinx, 1 = ASIC */ { int olddelay, a, b; + + w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */ + udelay(1500000); olddelay = pi->delay; pi->delay = 10; diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/jumbo linux/drivers/block/paride/jumbo --- v2.0.35/linux/drivers/block/paride/jumbo Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/paride/jumbo Sun Nov 15 10:32:52 1998 @@ -0,0 +1,70 @@ +#!/bin/sh +# +# This script can be used to build "jumbo" modules that contain the +# base PARIDE support, one protocol module and one high-level driver. +# +echo -n "High level driver [pcd] : " +read X +HLD=${X:-pcd} +# +echo -n "Protocol module [bpck] : " +read X +PROTO=${X:-bpck} +# +echo -n "Use MODVERSIONS [y] ? " +read X +UMODV=${X:-y} +# +echo -n "For SMP kernel [n] ? " +read X +USMP=${X:-n} +# +echo -n "Support PARPORT [n] ? " +read X +UPARP=${X:-n} +# +echo +# +case $USMP in + y* | Y* ) FSMP="-D__SMP__" + ;; + *) FSMP="" + ;; +esac +# +MODI="-include ../../../include/linux/modversions.h" +# +case $UMODV in + y* | Y* ) FMODV="-DMODVERSIONS $MODI" + ;; + *) FMODV="" + ;; +esac +# +case $UPARP in + y* | Y* ) FPARP="-DCONFIG_PARPORT" + ;; + *) FPARP="" + ;; +esac +# +TARG=$HLD-$PROTO.o +FPROTO=-DCONFIG_PARIDE_`echo "$PROTO" | tr [a-z] [A-Z]` +FK="-D__KERNEL__ -I ../../../include" +FLCH=-D_LINUX_CONFIG_H +# +echo cc $FK $FSMP $FLCH $FPARP $FPROTO -Wall -O2 -o Jb.o -c paride.c +cc $FK $FSMP $FLCH $FPARP $FPROTO -Wall -O2 -o Jb.o -c paride.c +# +echo cc $FK $FSMP -Wall -O2 -o Jp.o -c $PROTO.c +cc $FK $FSMP -Wall -O2 -o Jp.o -c $PROTO.c +# +echo cc $FK $FSMP $FMODV -DMODULE -DPARIDE_JUMBO -Wall -O2 -o Jd.o -c $HLD.c +cc $FK $FSMP $FMODV -DMODULE -DPARIDE_JUMBO -Wall -O2 -o Jd.o -c $HLD.c +# +echo ld -r -o $TARG Jp.o Jb.o Jd.o +ld -r -o $TARG Jp.o Jb.o Jd.o +# +# +rm Jp.o Jb.o Jd.o +# diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/on26.c linux/drivers/block/paride/on26.c --- v2.0.35/linux/drivers/block/paride/on26.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/on26.c Sun Nov 15 10:32:52 1998 @@ -10,10 +10,11 @@ /* Changes: 1.01 GRG 1998.05.06 init_proto, release_proto + 1.02 GRG 1998.09.23 updates for the -E rev chip */ -#define ON26_VERSION "1.01" +#define ON26_VERSION "1.02" #include #include @@ -91,8 +92,8 @@ } } -#define CCP(x) w0(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ - w0(0x87);w0(0x78);w0(x);w2(4); +#define CCP(x) w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ + w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff); static void on26_connect ( PIA *pi ) @@ -102,7 +103,6 @@ pi->saved_r2 = r2(); CCP(0x20); - w2(0xcd); w2(0xcc); w0(0xff); x = 8; if (pi->mode) x = 9; w0(2); P1; w0(8); P2; @@ -114,11 +114,62 @@ { if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); } else { w0(4); P1; w0(4); P1; } CCP(0x30); - w2(0xcd); w2(0xcc); w0(0xff); w0(pi->saved_r0); w2(pi->saved_r2); } +static int on26_test_port( PIA *pi) /* hard reset */ + +{ int i, m, d; + + pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + d = pi->delay; + m = pi->mode; + pi->delay = 5; + pi->mode = 0; + + w2(0xc); + + CCP(0x30); CCP(0); + + w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff); + i = ((r1() & 0xf0) << 4); w0(0x87); + i |= (r1() & 0xf0); w0(0x78); + w0(0x20);w2(4);w2(5); + i |= ((r1() & 0xf0) >> 4); + w2(4);w0(0xff); + + if (i == 0xb5f) { + + w0(2); P1; w0(0); P2; + w0(3); P1; w0(0); P2; + w0(2); P1; w0(8); P2; udelay(100); + w0(2); P1; w0(0xa); P2; udelay(100); + w0(2); P1; w0(8); P2; udelay(1000); + + on26_write_regr(pi,0,6,0xa0); + + for (i=0;i<100;i++) { + if (!(on26_read_regr(pi,0,7) & 0x80)) break; + udelay(100000); + } + + w0(4); P1; w0(4); P1; + } + + CCP(0x30); + + pi->delay = d; + pi->mode = m; + w0(pi->saved_r0); + w2(pi->saved_r2); + + return 5; +} + + static void on26_read_block( PIA *pi, char * buf, int count ) { int k, a, b; @@ -240,7 +291,7 @@ on26_read_block, on26_connect, on26_disconnect, - 0, + on26_test_port, 0, 0, on26_log_adapter, diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c --- v2.0.35/linux/drivers/block/paride/pcd.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/pcd.c Sun Nov 15 10:32:52 1998 @@ -65,10 +65,11 @@ (default "pcd") verbose This parameter controls the amount of logging - that is done while the driver probes for - devices. Set it to 0 for a quiet load, or 1 to - see all the progress messages. (default 0) - + that the driver will do. Set it to 0 for + normal operation, 1 to see autoprobe progress + messages, or 2 to see additional debugging + output. (default 0) + nice This parameter controls the driver's use of idle CPU time, at the expense of some speed. @@ -95,10 +96,13 @@ standard for clearing error status. Use spinlocks. Eliminate sti(). 1.03 GRG 1998.06.16 Eliminated an Ugh + 1.04 GRG 1998.08.15 Added extra debugging, improvements to + pcd_completion, use HZ in loop timing + 1.05s GRG 1998.09.24 Added jumbo support, adjust reset timeout */ -#define PCD_VERSION "1.03s" +#define PCD_VERSION "1.05s" #define PCD_MAJOR 46 #define PCD_NAME "pcd" #define PCD_UNITS 4 @@ -180,9 +184,9 @@ #define PCD_RETRIES 5 #define PCD_TMO 800 /* timeout in jiffies */ #define PCD_DELAY 50 /* spin delay in uS */ -#define PCD_READY_TMO 20 +#define PCD_READY_TMO 20 /* in seconds */ -#define PCD_SPIN (10000/PCD_DELAY)*PCD_TMO +#define PCD_SPIN (1000000*PCD_TMO)/(HZ*PCD_DELAY) #define IDE_ERR 0x01 #define IDE_DRQ 0x08 @@ -246,6 +250,8 @@ static int pcd_count; /* number of blocks still to do */ static char * pcd_buf; /* buffer for request in progress */ +static int pcd_warned = 0; /* Have we logged a phase warning ? */ + /* kernel glue structures */ static struct file_operations pcd_fops = { @@ -405,6 +411,12 @@ { int err; +#ifdef PARIDE_JUMBO + { extern paride_init(); + paride_init(); + } +#endif + err = pcd_init(); return err; @@ -479,23 +491,51 @@ static int pcd_completion( int unit, char * buf, char * fun ) -{ int r, s, n; +{ int r, d, p, n, k, j; - r = pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,fun,"completion"); + r = -1; k = 0; j = 0; - if ((RR(0,2)&2) && (RR(0,7)&IDE_DRQ)) { - n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc); - pi_read_block(PI,buf,n); + if (!pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR, + fun,"completion")) { + r = 0; + while (RR(0,7)&IDE_DRQ) { + d = (RR(0,4)+256*RR(0,5)); + n = ((d+3)&0xfffc); + p = RR(0,2)&3; + + if ((p == 2) && (n > 0) && (j == 0)) { + pi_read_block(PI,buf,n); + if (verbose > 1) + printk("%s: %s: Read %d bytes\n",PCD.name,fun,n); + r = 0; j++; + } else { + if (verbose > 1) + printk("%s: %s: Unexpected phase %d, d=%d, k=%d\n", + PCD.name,fun,p,d,k); + if ((verbose < 2) && !pcd_warned) { + pcd_warned = 1; + printk("%s: WARNING: ATAPI phase errors\n",PCD.name); + } + udelay(1000); + } + if (k++ > PCD_TMO) { + printk("%s: Stuck DRQ\n",PCD.name); + break; + } + if (pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR, + fun,"completion")) { + r = -1; + break; + } + } } - - s = pcd_wait(unit,IDE_BUSY,IDE_READY|IDE_ERR,fun,"data done"); - + pi_disconnect(PI); - return (r?r:s); + return r; } -static void pcd_req_sense( int unit, int quiet ) +static void pcd_req_sense( int unit, char *fun ) { char rs_cmd[12] = { 0x03,0,0,0,16,0,0,0,0,0,0,0 }; char buf[16]; @@ -507,8 +547,8 @@ PCD.last_sense = -1; if (!r) { - if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n", - PCD.name,buf[2]&0xf,buf[12],buf[13]); + if (fun) printk("%s: %s: Sense key: %x, ASC: %x, ASQ: %x\n", + PCD.name,fun,buf[2]&0xf,buf[12],buf[13]); PCD.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8) | ((buf[13]&0xff)<<16) ; } @@ -521,12 +561,12 @@ r = pcd_command(unit,cmd,dlen,fun); udelay(1000); if (!r) r = pcd_completion(unit,buf,fun); - if (r) pcd_req_sense(unit,!fun); + if (r) pcd_req_sense(unit,fun); return r; } -#define DBMSG(msg) NULL +#define DBMSG(msg) ((verbose>1)?(msg):NULL) static void pcd_lock(int unit) @@ -558,7 +598,7 @@ pcd_atapi(unit,ej_cmd,0,pcd_scratch,"eject"); } -#define PCD_RESET_TMO 30 /* in tenths of a second */ +#define PCD_RESET_TMO 100 /* in tenths of a second */ static void pcd_sleep( int cs ) diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c --- v2.0.35/linux/drivers/block/paride/pd.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/pd.c Sun Nov 15 10:32:52 1998 @@ -113,10 +113,12 @@ 1.02 GRG 1998.05.06 SMP spinlock changes, Added slave support 1.03 GRG 1998.06.16 Eliminate an Ugh. + 1.04s GRG 1998.09.24 Added jumbo support + Use HZ in loop timings, extra debugging */ -#define PD_VERSION "1.03s" +#define PD_VERSION "1.04s" #define PD_MAJOR 45 #define PD_NAME "pd" #define PD_UNITS 4 @@ -218,7 +220,7 @@ #define PD_TMO 800 /* interrupt timeout in jiffies */ #define PD_SPIN_DEL 50 /* spin delay in micro-seconds */ -#define PD_SPIN (10000/PD_SPIN_DEL)*PD_TMO +#define PD_SPIN (1000000*PD_TMO)/(HZ*PD_SPIN_DEL) #define STAT_ERR 0x00001 #define STAT_INDEX 0x00002 @@ -595,6 +597,12 @@ { int err, unit; +#ifdef PARIDE_JUMBO + { extern paride_init(); + paride_init(); + } +#endif + err = pd_init(); if (err) return err; @@ -653,7 +661,7 @@ udelay(250); } -#define DBMSG(msg) NULL +#define DBMSG(msg) ((verbose>1)?(msg):NULL) static int pd_wait_for( int unit, int w, char * msg ) /* polled wait */ diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c --- v2.0.35/linux/drivers/block/paride/pf.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/pf.c Sun Nov 15 10:32:52 1998 @@ -111,10 +111,12 @@ Small change in pf_completion to round up transfer size. 1.02 GRG 1998.06.16 Eliminated an Ugh + 1.03s GRG 1998.09.24 Added jumbo support + Use HZ in loop timings, extra debugging */ -#define PF_VERSION "1.02s" +#define PF_VERSION "1.03s" #define PF_MAJOR 47 #define PF_NAME "pf" #define PF_UNITS 4 @@ -211,7 +213,7 @@ #define PF_TMO 800 /* interrupt timeout in jiffies */ #define PF_SPIN_DEL 50 /* spin delay in micro-seconds */ -#define PF_SPIN (10000/PF_SPIN_DEL)*PF_TMO +#define PF_SPIN (1000000*PF_TMO)/(HZ*PF_SPIN_DEL) #define STAT_ERR 0x00001 #define STAT_INDEX 0x00002 @@ -495,6 +497,12 @@ { int err; +#ifdef PARIDE_JUMBO + { extern paride_init(); + paride_init(); + } +#endif + err = pf_init(); return err; @@ -616,7 +624,7 @@ return r; } -#define DBMSG(msg) NULL +#define DBMSG(msg) ((verbose>1)?(msg):NULL) static void pf_lock(int unit, int func) diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pg.c linux/drivers/block/paride/pg.c --- v2.0.35/linux/drivers/block/paride/pg.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/pg.c Sun Nov 15 10:32:52 1998 @@ -38,10 +38,10 @@ To use this device, you must have the following device special files defined: - /dev/pg0 b 97 0 - /dev/pg1 b 97 1 - /dev/pg2 b 97 2 - /dev/pg3 b 97 3 + /dev/pg0 c 97 0 + /dev/pg1 c 97 1 + /dev/pg2 c 97 2 + /dev/pg3 c 97 3 (You'll need to change the 97 to something else if you use the 'major' parameter to install the driver on a different @@ -121,9 +121,11 @@ /* Changes: 1.01 GRG 1998.06.16 Bug fixes + 1.02 GRG 1998.09.24 Added jumbo support + */ -#define PG_VERSION "1.01s" +#define PG_VERSION "1.02s" #define PG_MAJOR 97 #define PG_NAME "pg" #define PG_UNITS 4 @@ -318,6 +320,12 @@ int init_module(void) { int err; + +#ifdef PARIDE_JUMBO + { extern paride_init(); + paride_init(); + } +#endif err = pg_init(); diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pt.c linux/drivers/block/paride/pt.c --- v2.0.35/linux/drivers/block/paride/pt.c Sun Nov 15 10:49:32 1998 +++ linux/drivers/block/paride/pt.c Sun Nov 15 10:32:52 1998 @@ -74,10 +74,11 @@ (default "pt"). verbose This parameter controls the amount of logging - that is done while the driver probes for - devices. Set it to 0 for a quiet load, or 1 to - see all the progress messages. (default 0) - + that the driver will do. Set it to 0 for + normal operation, 1 to see autoprobe progress + messages, or 2 to see additional debugging + output. (default 0) + If this driver is built into the kernel, you can use the following command line parameters, with the same values as the corresponding module parameters listed above: @@ -99,10 +100,13 @@ for clearing error status. Eliminate sti(); 1.02 GRG 1998.06.16 Eliminate an Ugh. - + 1.03 GRG 1998.08.15 Adjusted PT_TMO, use HZ in loop timing, + extra debugging + 1.04 GRG 1998.09.24 Repair minor coding error, added jumbo support + */ -#define PT_VERSION "1.02s" +#define PT_VERSION "1.04s" #define PT_MAJOR 96 #define PT_NAME "pt" #define PT_UNITS 4 @@ -168,13 +172,13 @@ #include "paride.h" #define PT_MAX_RETRIES 5 -#define PT_TMO 800 /* interrupt timeout in jiffies */ +#define PT_TMO 3000 /* interrupt timeout in jiffies */ #define PT_SPIN_DEL 50 /* spin delay in micro-seconds */ -#define PT_RESET_TMO 30 /* 3 seconds */ +#define PT_RESET_TMO 30 /* 30 seconds */ #define PT_READY_TMO 60 /* 60 seconds */ #define PT_REWIND_TMO 1200 /* 20 minutes */ -#define PT_SPIN (10000/PT_SPIN_DEL)*PT_TMO +#define PT_SPIN ((1000000/(HZ*PT_SPIN_DEL))*PT_TMO) #define STAT_ERR 0x00001 #define STAT_INDEX 0x00002 @@ -317,6 +321,12 @@ { int err; +#ifdef PARIDE_JUMBO + { extern paride_init(); + paride_init(); + } +#endif + err = pt_init(); return err; @@ -498,7 +508,7 @@ pt_media_access_cmd(unit,PT_TMO,wm_cmd,"write filemark"); } -#define DBMSG(msg) NULL +#define DBMSG(msg) ((verbose>1)?(msg):NULL) static int pt_reset( int unit ) @@ -573,8 +583,8 @@ char *ms[2] = {"master","slave"}; char mf[10], id[18]; char id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0}; - char ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,128,0,0,0,0,0,0,0}; - char ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,128,0,0,0}; + char ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,36,0,0,0,0,0,0,0}; + char ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,36,0,0,0}; char buf[36]; s = pt_atapi(unit,id_cmd,36,buf,"identify"); diff -u --recursive --new-file v2.0.35/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c --- v2.0.35/linux/drivers/cdrom/sonycd535.c Sun Nov 15 10:49:33 1998 +++ linux/drivers/cdrom/sonycd535.c Sun Nov 15 10:32:53 1998 @@ -156,6 +156,8 @@ # define CDU535_MESSAGE_NAME "Sony CDU-535" #endif +#define CDU535_BLOCK_SIZE 2048 + #ifndef MAX_SPINUP_RETRY # define MAX_SPINUP_RETRY 3 /* 1 is sufficient for most drives... */ #endif @@ -594,8 +596,6 @@ * it returns one of the standard error returns. ***************************************************************************/ -static int sonycd535_block_size = 2048; - static int seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2], Byte **buff, int buf_size) @@ -607,7 +607,7 @@ Byte *data_buff; int sector_count = 0; - if (buf_size < sonycd535_block_size * n_blocks) + if (buf_size < CDU535_BLOCK_SIZE * n_blocks) return NO_ROOM; set_drive_mode(SONY535_CDROM_DRIVE_MODE, status); @@ -632,7 +632,7 @@ if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) { /* data is ready, read it */ data_buff = buff[sector_count++]; - for (i = 0; i < sonycd535_block_size; i++) + for (i = 0; i < CDU535_BLOCK_SIZE; i++) *data_buff++ = inb(data_reg); /* unrolling this loop does not seem to help */ break; /* exit the timeout loop */ } @@ -645,7 +645,7 @@ /* read all the data, now read the status */ if ((i = read_exec_status(status)) != 0) return i; - return sonycd535_block_size * sector_count; + return CDU535_BLOCK_SIZE * sector_count; } /* seek_and_read_N_blocks() */ /**************************************************************************** @@ -782,7 +782,7 @@ * The OS calls this to perform a read or write operation to the drive. * Write obviously fail. Reads to a read ahead of sony_buffer_size * bytes to help speed operations. This especially helps since the OS - * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most + * may use 1024 byte blocks and the drive uses 2048 byte blocks. Since most * data access on a CD is done sequentially, this saves a lot of operations. */ static void @@ -879,7 +879,7 @@ * seek_and_read_N_blocks for the various cases. */ int readStatus = seek_and_read_N_blocks(params, read_size, - status, sony_buffer, (read_size * 2048)); + status, sony_buffer, (read_size * CDU535_BLOCK_SIZE)); if (0 <= readStatus) /* Good data; common case, placed first */ break; if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) { @@ -1485,6 +1485,8 @@ NULL /* revalidate */ }; +static int sonycd535_block_size = CDU535_BLOCK_SIZE; + /* * Initialize the driver. */ @@ -1583,7 +1585,7 @@ if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) { /* set the drive mode successful, we are set! */ sony_buffer_size = SONY535_BUFFER_SIZE; - sony_buffer_sectors = sony_buffer_size / 2048; + sony_buffer_sectors = sony_buffer_size / CDU535_BLOCK_SIZE; printk(KERN_INFO CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s", drive_config.vendor_id, @@ -1621,7 +1623,8 @@ return -ENOMEM; } for (i = 0; i < sony_buffer_sectors; i++) { - sony_buffer[i] = (Byte *)kmalloc(2048, GFP_KERNEL); + sony_buffer[i] = + (Byte *)kmalloc(CDU535_BLOCK_SIZE, GFP_KERNEL); if (sony_buffer[i] == NULL) { while (--i>=0) kfree(sony_buffer[i]); @@ -1684,7 +1687,7 @@ release_region(sony535_cd_base_io, 4); for (i = 0; i < sony_buffer_sectors; i++) - kfree_s(sony_buffer[i], 2048); + kfree_s(sony_buffer[i], CDU535_BLOCK_SIZE); kfree_s(sony_buffer, 4 * sony_buffer_sectors); kfree_s(last_sony_subcode, sizeof *last_sony_subcode); kfree_s(sony_toc, sizeof *sony_toc); diff -u --recursive --new-file v2.0.35/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.0.35/linux/drivers/char/Config.in Mon Jul 13 13:46:26 1998 +++ linux/drivers/char/Config.in Sun Nov 15 10:32:53 1998 @@ -7,6 +7,9 @@ tristate 'Standard/generic serial support' CONFIG_SERIAL bool 'Digiboard PC/Xx Support' CONFIG_DIGI tristate 'Cyclades async mux support' CONFIG_CYCLADES +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m +fi bool 'Stallion multiport serial support' CONFIG_STALDRV if [ "$CONFIG_STALDRV" = "y" ]; then tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION diff -u --recursive --new-file v2.0.35/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.0.35/linux/drivers/char/Makefile Sun Nov 15 10:49:33 1998 +++ linux/drivers/char/Makefile Sun Nov 15 10:32:53 1998 @@ -86,6 +86,14 @@ endif endif +ifeq ($(CONFIG_ISI),y) +L_OBJS += isicom.o +else + ifeq ($(CONFIG_ISI),m) + M_OBJS += isicom.o + endif +endif + ifeq ($(CONFIG_ATIXL_BUSMOUSE),y) M = y L_OBJS += atixlmouse.o diff -u --recursive --new-file v2.0.35/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c --- v2.0.35/linux/drivers/char/apm_bios.c Sun Nov 15 10:49:33 1998 +++ linux/drivers/char/apm_bios.c Sun Nov 15 10:32:53 1998 @@ -40,6 +40,8 @@ * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth * ); improve interaction between * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4 + * 1.2a: Fix OOPs on power off with no APM BIOS + * Jan Echternach * * Reference: * @@ -444,16 +446,24 @@ return APM_SUCCESS; } -int apm_set_power_state(u_short state) +static int apm_set_power_state(u_short state) { u_short error; - + APM_SET_POWER_STATE(state, error); if (error & 0xff) return (error >> 8); return APM_SUCCESS; } +#ifdef CONFIG_APM_POWER_OFF +void apm_power_off(void) +{ + if (apm_enabled) + (void) apm_set_power_state(APM_STATE_OFF); +} +#endif + #ifdef CONFIG_APM_DISPLAY_BLANK /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */ static int apm_set_display_power_state(u_short state) @@ -1100,6 +1110,16 @@ } #endif +static int apm_disabled = 0; + +void apm_setup(char *str, int *ints) +{ + if(strcmp(str,"off")==0) + apm_disabled=1; + if(strcmp(str,"on")==0) + apm_disabled=0; +} + void apm_bios_init(void) { unsigned short bx; @@ -1108,6 +1128,12 @@ unsigned short error; char * power_stat; char * bat_stat; + + if (apm_disabled == 1) + { + printk("APM disabled.\n"); + return; + } if (apm_bios_info.version == 0) { printk("APM BIOS not found.\n"); diff -u --recursive --new-file v2.0.35/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c --- v2.0.35/linux/drivers/char/cyclades.c Mon Oct 7 22:03:17 1996 +++ linux/drivers/char/cyclades.c Sun Nov 15 10:32:53 1998 @@ -1,13 +1,17 @@ +#define BLOCKMOVE +#define Z_WAKE static char rcsid[] = -"$Revision: 1.36.3.9 $$Date: 1996/10/07 19:47:13 $"; +"$Revision: 2.1.1.10 $$Date: 1998/11/12 16:08:23 $"; + /* * linux/drivers/char/cyclades.c * * This file contains the driver for the Cyclades Cyclom-Y multiport * serial boards. * - * Maintained by Marcio Saito (marcio@cyclades.com) and - * Randolph Bentson (bentson@grieg.seaslug.org) + * Maintained by Ivan Passos (ivan@cyclades.com), + * Marcio Saito (marcio@cyclades.com) and + * Randolph Bentson (bentson@grieg.seaslug.org). * * For Technical support and installation problems, please send e-mail * to support@cyclades.com. @@ -22,11 +26,239 @@ * This module exports the following rs232 io functions: * int cy_init(void); * int cy_open(struct tty_struct *tty, struct file *filp); + * and the following functions for modularization. + * int init_module(void); + * void cleanup_module(void); * * $Log: cyclades.c,v $ - * Revision 1.36.3.9 1996/10/07 19:47:13 bentson - * add MOD_DEC_USE_COUNT in one return from cy_close (as - * noted by Jon Lewis ) + * Revision: 2.1.1.10 1998/11/12 16:08:23 ivan + * cy_close function now resets (correctly) the tty->closing flag; + * JIFFIES_DIFF macro fixed. + * + * Revision 2.1.1.9 1998/09/02 14:47:01 ivan + * Fixed bug in cy_close function, which was not informing HW of + * which port should have the reception disabled before doing so; + * fixed Cyclom-8YoP hardware detection bug. + * + * Revision 2.1.1.8 1998/08/20 17:04:54 ivan + * Fixed bug in cy_close function, which causes malfunction + * of one of the first 4 ports when a higher port is closed + * (Cyclom-Y only). + * + * Revision 2.1.1.7 1998/08/10 17:01:45 ivan + * Fixed Cyclom-4Yo hardware detection bug. + * + * Revision 2.1.1.6 1998/08/04 12:19:36 ivan + * cyy_interrupt changed once more to avoid occurence of kernel + * oopses during PPP operation. + * + * Revision 2.1.1.5 1998/08/03 16:55:59 ivan + * /proc/cyclades implementation (only for monolythic driver) + * with great collaboration of Marc Lewis ; + * cyy_interrupt was changed to avoid occurence of kernel oopses + * during PPP operation. + * + * Revision 2.1.1.4 1998/06/01 18:01:19 ivan + * data loss prevention revisited (cy_wait_until_sent created); + * MOD_COUNT bug (which caused the usage decrementing not to work + * properly) fixed. + * + * Revision 2.1.1.3b 1998/05/14 10:14:00 ivan + * temporary workaround for losing SIGHUPs in PPP connections + * (Cyclom-Y only). + * + * Revision 2.1.1.3a 1998/03/16 18:01:12 ivan + * cleaned up the data loss fix; + * fixed XON/XOFF handling once more (Cyclades-Z); + * general revision in the driver routines; + * introduction of a mechanism to prevent data loss with slow + * printers, by forcing a delay before closing the port. + * + * Revision 2.1.1.2a 1998/02/17 16:50:00 ivan + * fixed detection/handling of new CD1400 in Ye boards; + * fixed XON/XOFF handling (Cyclades-Z); + * fixed data loss caused by a premature port close; + * introduction of a flag that holds the CD1400 version ID per port + * (used by the CYGETCD1400VER new ioctl). + * + * Revision 2.1.1.1a 1997/12/03 17:31:19 ivan + * Code review for the module cleanup routine; + * fixed RTS and DTR status report for new CD1400's in get_modem_info; + * includes anonymous changes regarding signal_pending. + * + * Revision 2.1 1997/11/01 17:42:41 ivan + * Changes in the driver to support Alpha systems (except 8Zo V_1); + * BREAK fix for the Cyclades-Z boards; + * driver inactivity control by FW implemented; + * introduction of flag that allows driver to take advantage of + * a special CD1400 feature related to HW flow control; + * added support for the CD1400 rev. J (Cyclom-Y boards); + * introduction of ioctls to: + * - control the rtsdtr_inv flag (Cyclom-Y); + * - control the rflow flag (Cyclom-Y); + * - adjust the polling interval (Cyclades-Z); + * + * Revision 1.36.4.33 1997/06/27 19:00:00 ivan + * Fixes related to kernel version conditional + * compilation. + * + * Revision 1.36.4.32 1997/06/14 19:30:00 ivan + * Compatibility issues between kernels 2.0.x and + * 2.1.x (mainly related to clear_bit function). + * + * Revision 1.36.4.31 1997/06/03 15:30:00 ivan + * Changes to define the memory window according to the + * board type. + * + * Revision 1.36.4.30 1997/05/16 15:30:00 daniel + * Changes to suport new cycladesZ boards. + * + * Revision 1.36.4.29 1997/05/12 11:30:00 daniel + * Merge of Bentson's and Daniel's version 1.36.4.28. + * Corrects bug in cy_detect_pci: check if there are more + * ports than the number of static structs allocated. + * Warning message during initialization if this driver is + * used with the new generation of cycladesZ boards. Those + * will be supported only in next release of the driver. + * Corrects bug in cy_detect_pci and cy_detect_isa that + * returned wrong number of VALID boards, when a cyclomY + * was found with no serial modules connected. + * Changes to use current (2.1.x) kernel subroutine names + * and created macros for compilation with 2.0.x kernel, + * instead of the other way around. + * + * Revision 1.36.4.28 1997/05/?? ??:00:00 bentson + * Change queue_task_irq_off to queue_task_irq. + * The inline function queue_task_irq_off (tqueue.h) + * was removed from latest releases of 2.1.x kernel. + * Use of macro __initfunc to mark the initialization + * routines, so memory can be reused. + * Also incorporate implementation of critical region + * in function cleanup_module() created by anonymous + * linuxer. + * + * Revision 1.36.4.28 1997/04/25 16:00:00 daniel + * Change to support new firmware that solves DCD problem: + * application could fail to receive SIGHUP signal when DCD + * varying too fast. + * + * Revision 1.36.4.27 1997/03/26 10:30:00 daniel + * Changed for suport linux versions 2.1.X. + * Backward compatible with linux versions 2.0.X. + * Corrected illegal use of filler field in + * CH_CTRL struct. + * Deleted some debug messages. + * + * Revision 1.36.4.26 1997/02/27 12:00:00 daniel + * Included check for NULL tty pointer in cyz_poll. + * + * Revision 1.36.4.25 1997/02/26 16:28:30 bentson + * Bill Foster at Blarg! Online services noticed that + * some of the switch elements of -Z modem control + * lacked a closing "break;" + * + * Revision 1.36.4.24 1997/02/24 11:00:00 daniel + * Changed low water threshold for buffer xmit_buf + * + * Revision 1.36.4.23 1996/12/02 21:50:16 bentson + * Marcio provided fix to modem status fetch for -Z + * + * Revision 1.36.4.22 1996/10/28 22:41:17 bentson + * improve mapping of -Z control page (thanks to Steve + * Price for help on this) + * + * Revision 1.36.4.21 1996/09/10 17:00:10 bentson + * shift from CPU-bound to memcopy in cyz_polling operation + * + * Revision 1.36.4.20 1996/09/09 18:30:32 Bentson + * Added support to set and report higher speeds. + * + * Revision 1.36.4.19c 1996/08/09 10:00:00 Marcio Saito + * Some fixes in the HW flow control for the BETA release. + * Don't try to register the IRQ. + * + * Revision 1.36.4.19 1996/08/08 16:23:18 Bentson + * make sure "cyc" appears in all kernel messages; all soft interrupts + * handled by same routine; recognize out-of-band reception; comment + * out some diagnostic messages; leave RTS/CTS flow control to hardware; + * fix race condition in -Z buffer management; only -Y needs to explictly + * flush chars; tidy up some startup messages; + * + * Revision 1.36.4.18 1996/07/25 18:57:31 bentson + * shift MOD_INC_USE_COUNT location to match + * serial.c; purge some diagnostic messages; + * + * Revision 1.36.4.17 1996/07/25 18:01:08 bentson + * enable modem status messages and fetch & process them; note + * time of last activity type for each port; set_line_char now + * supports more than line 0 and treats 0 baud correctly; + * get_modem_info senses rs_status; + * + * Revision 1.36.4.16 1996/07/20 08:43:15 bentson + * barely works--now's time to turn on + * more features 'til it breaks + * + * Revision 1.36.4.15 1996/07/19 22:30:06 bentson + * check more -Z board status; shorten boot message + * + * Revision 1.36.4.14 1996/07/19 22:20:37 bentson + * fix reference to ch_ctrl in startup; verify return + * values from cyz_issue_cmd and cyz_update_channel; + * more stuff to get modem control correct; + * + * Revision 1.36.4.13 1996/07/11 19:53:33 bentson + * more -Z stuff folded in; re-order changes to put -Z stuff + * after -Y stuff (to make changes clearer) + * + * Revision 1.36.4.12 1996/07/11 15:40:55 bentson + * Add code to poll Cyclades-Z. Add code to get & set RS-232 control. + * Add code to send break. Clear firmware ID word at startup (so + * that other code won't talk to inactive board). + * + * Revision 1.36.4.11 1996/07/09 05:28:29 bentson + * add code for -Z in set_line_char + * + * Revision 1.36.4.10 1996/07/08 19:28:37 bentson + * fold more -Z stuff (or in some cases, error messages) + * into driver; add text to "don't know what to do" messages. + * + * Revision 1.36.4.9 1996/07/08 18:38:38 bentson + * moved compile-time flags near top of file; cosmetic changes + * to narrow text (to allow 2-up printing); changed many declarations + * to "static" to limit external symbols; shuffled code order to + * coalesce -Y and -Z specific code, also to put internal functions + * in order of tty_driver structure; added code to recognize -Z + * ports (and for moment, do nothing or report error); add cy_startup + * to parse boot command line for extra base addresses for ISA probes; + * + * Revision 1.36.4.8 1996/06/25 17:40:19 bentson + * reorder some code, fix types of some vars (int vs. long), + * add cy_setup to support user declared ISA addresses + * + * Revision 1.36.4.7 1996/06/21 23:06:18 bentson + * dump ioctl based firmware load (it's now a user level + * program); ensure uninitialzed ports cannot be used + * + * Revision 1.36.4.6 1996/06/20 23:17:19 bentson + * rename vars and restructure some code + * + * Revision 1.36.4.5 1996/06/14 15:09:44 bentson + * get right status back after boot load + * + * Revision 1.36.4.4 1996/06/13 19:51:44 bentson + * successfully loads firmware + * + * Revision 1.36.4.3 1996/06/13 06:08:33 bentson + * add more of the code for the boot/load ioctls + * + * Revision 1.36.4.2 1996/06/11 21:00:51 bentson + * start to add Z functionality--starting with ioctl + * for loading firmware + * + * Revision 1.36.4.1 1996/06/10 18:03:02 bentson + * added code to recognize Z/PCI card at initialization; report + * presence, but card is not initialized (because firmware needs + * to be loaded) * * Revision 1.36.3.8 1996/06/07 16:29:00 bentson * starting minor number at zero; added missing verify_area @@ -261,6 +493,63 @@ * */ +/* If you need to install more boards than NR_CARDS, change the constant + in the definition below. No other change is necessary to support up to + eight boards. Beyond that you'll have to extend cy_isa_addresses. */ + +#define NR_CARDS 4 + +/* + If the total number of ports is larger than NR_PORTS, change this + constant in the definition below. No other change is necessary to + support more boards/ports. */ + +#define NR_PORTS 128 + +#define ZE_V1_NPORTS 64 +#define ZO_V1 0 +#define ZO_V2 1 +#define ZE_V1 2 + +#define SERIAL_PARANOIA_CHECK +#undef CY_DEBUG_OPEN +#undef CY_DEBUG_THROTTLE +#undef CY_DEBUG_OTHER +#undef CY_DEBUG_IO +#undef CY_DEBUG_COUNT +#undef CY_DEBUG_DTR +#undef CY_DEBUG_WAIT_UNTIL_SENT +#undef CY_16Y_HACK +#undef CY_ENABLE_MONITORING +#undef CY_PCI_DEBUG + +#if 0 +#define PAUSE __asm__("nop"); +#else +#define PAUSE ; +#endif + +#define cy_min(a,b) (((a)<(b))?(a):(b)) + +#if 0 +/******** + * For the next two macros, it is assumed that the buffer size is a + * power of 2 + ********/ + +#define CHARS_IN_BUF(buf_ctrl) \ + ((cy_readl(&buf_ctrl->rx_put) - \ + cy_readl(&buf_ctrl->rx_get) + \ + cy_readl(&buf_ctrl->rx_bufsize)) & \ + (cy_readl(&buf_ctrl->rx_bufsize) - 1)) + +#define SPACE_IN_BUF(buf_ctrl) \ + ((cy_readl(&buf_ctrl->tx_get) - \ + cy_readl(&buf_ctrl->tx_put) + \ + cy_readl(&buf_ctrl->tx_bufsize) - 1) & \ + (cy_readl(&buf_ctrl->tx_bufsize) - 1)) +#endif + #include #include @@ -289,30 +578,44 @@ #include #include -#define small_delay(x) for(j=0;j +#ifdef CONFIG_PROC_FS +#include +#include +#endif -#define SERIAL_PARANOIA_CHECK -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_THROTTLE -#undef SERIAL_DEBUG_OTHER -#undef SERIAL_DEBUG_IO -#undef SERIAL_DEBUG_COUNT -#undef SERIAL_DEBUG_DTR -#undef CYCLOM_16Y_HACK -#undef CYCLOM_ENABLE_MONITORING +#define __initfunc(__arginit) __arginit +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#define cy_get_user get_fs_long +#define cy_put_user put_fs_long +#define ioremap vremap #ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif -#define WAKEUP_CHARS 256 +#define IS_CYC_Z(card) ((card).num_chips == -1) + +#define Z_FPGA_CHECK(card) \ + ((cy_readl(&((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0) + +#define ISZLOADED(card) (((ZO_V1==cy_readl(&((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->mail_box_0)) || \ + Z_FPGA_CHECK(card)) && \ + (ZFIRM_ID==cy_readl(&((struct FIRM_ID *) \ + ((card).base_addr+ID_ADDRESS))->signature))) + +#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256) #define STD_COM_FLAGS (0) #define SERIAL_TYPE_NORMAL 1 #define SERIAL_TYPE_CALLOUT 2 +#define JIFFIES_DIFF(n, j) ((j) - (n)) DECLARE_TASK_QUEUE(tq_cyclades); @@ -321,75 +624,41 @@ static volatile int cy_irq_triggered; static volatile int cy_triggered; static int cy_wild_int_mask; -static unsigned char *intr_base_addr; +static volatile ucchar *intr_base_addr; - -/* This is the address lockup table. The driver will probe for Cyclom-Y/ISA - boards at all addresses in here. If you want the driver to probe addresses - in a different address, add it to this table. - If the driver is probing some other board and causing problems, remove the - address from this table. */ +/* This is the address lookup table. The driver will probe for + Cyclom-Y/ISA boards at all addresses in here. If you want the + driver to probe addresses at a different address, add it to + this table. If the driver is probing some other board and + causing problems, remove the offending address from this table. + The cy_setup function extracts additional addresses from the + boot options line. The form is "cyclades=address,address..." +*/ static unsigned char *cy_isa_addresses[] = { - (unsigned char *) 0xD0000, - (unsigned char *) 0xD2000, - (unsigned char *) 0xD4000, - (unsigned char *) 0xD6000, - (unsigned char *) 0xD8000, - (unsigned char *) 0xDA000, - (unsigned char *) 0xDC000, - (unsigned char *) 0xDE000, + (unsigned char *) 0xD0000, + (unsigned char *) 0xD2000, + (unsigned char *) 0xD4000, + (unsigned char *) 0xD6000, + (unsigned char *) 0xD8000, + (unsigned char *) 0xDA000, + (unsigned char *) 0xDC000, + (unsigned char *) 0xDE000, + 0,0,0,0,0,0,0,0 }; -#define NR_ISA_ADDRESSES (sizeof(cy_isa_addresses)/sizeof(unsigned char *)) +#define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*)) /* This is the per-card data structure containing address, irq, number of - channels, etc. This driver supports a maximum of NR_CARDS cards. If - you need to install more boards, change this constant in the definition - below. No other change is necessary to support more boards. */ - -#define NR_CARDS 4 - + channels, etc. This driver supports a maximum of NR_CARDS cards. +*/ static struct cyclades_card cy_card[NR_CARDS]; /* This is the per-channel data structure containing pointers, flags - and variables for the port. This driver supports a maximum of NR_PORTS. - If the total number of ports is larger than NR_PORTS, change this - constant in the definition below. No other change is necessary to - support more boards/ports. */ - -#define NR_PORTS 64 - + and variables for the port. This driver supports a maximum of NR_PORTS. +*/ static struct cyclades_port cy_port[NR_PORTS]; -/* The Cyclom-Ye has placed the sequential chips in non-sequential - * address order. This look-up table overcomes that problem. - */ -static int cy_chip_offset [] = - { 0x0000, - 0x0400, - 0x0800, - 0x0C00, - 0x0200, - 0x0600, - 0x0A00, - 0x0E00 - }; - -/* PCI related definitions */ - -static unsigned short cy_pci_nboard = 0; -static unsigned short cy_isa_nboard = 0; -static unsigned short cy_nboard = 0; -static unsigned short cy_pci_dev_id[] = { - PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ - PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ - 0 /* end of table */ - }; - -int cy_detect_isa(void); -int cy_detect_pci(void); - -static int cy_next_channel = 0; /* next minor available */ +static int cy_next_channel = 0; /* next minor available */ static int serial_refcount; @@ -400,17 +669,18 @@ /* This is the per-irq data structure, it maps an irq to the corresponding card */ -struct cyclades_card *IRQ_cards[16]; +static struct cyclades_card *IRQ_cards[16]; /* * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the memcpy_fromfs blocks while swapping in a page, + * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. + * memory if large numbers of serial ports are open. This buffer is + * allocated when the first cy_open occurs. */ static unsigned char *tmp_buf = 0; static struct semaphore tmp_buf_sem = MUTEX; @@ -419,83 +689,173 @@ * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra * are accessed via settings in info->flags. - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - * HI VHI + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + * HI VHI + * 20 */ static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, - 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, - 0}; - -static char baud_co[] = { /* 25 MHz clock option table */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, - 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -static char baud_bpr[] = { /* 25 MHz baud rate period table */ - 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, - 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, + 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, + 230400, 0}; + +static char baud_co_25[] = { /* 25 MHz clock option table */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static char baud_bpr_25[] = { /* 25 MHz baud rate period table */ + 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, + 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; + +static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, + 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00}; + +static char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ + 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, + 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, + 0x21}; static char baud_cor3[] = { /* receive threshold */ - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, + 0x07}; + +/* + * The Cyclades driver implements HW flow control as any serial driver. + * The cyclades_port structure member rflow and the vector rflow_thr + * allows us to take advantage of a special feature in the CD1400 to avoid + * data loss even when the system interrupt latency is too high. These flags + * are to be used only with very special applications. Setting these flags + * requires the use of a special cable (DTR and RTS reversed). In the new + * CD1400-based boards (rev. 6.00 or later), there is no need for special + * cables. + */ + +static char rflow_thr[] = { /* rflow threshold */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a}; +/* The Cyclom-Ye has placed the sequential chips in non-sequential + * address order. This look-up table overcomes that problem. + */ +static int cy_chip_offset [] = + { 0x0000, + 0x0400, + 0x0800, + 0x0C00, + 0x0200, + 0x0600, + 0x0A00, + 0x0E00 + }; +/* PCI related definitions */ -static void shutdown(struct cyclades_port *); -static int startup (struct cyclades_port *); -static void cy_throttle(struct tty_struct *); -static void cy_unthrottle(struct tty_struct *); -static void config_setup(struct cyclades_port *); -#ifdef CYCLOM_SHOW_STATUS +static unsigned short cy_pci_nboard = 0; +static unsigned short cy_isa_nboard = 0; +static unsigned short cy_nboard = 0; +static unsigned short cy_pci_dev_id[] = { + PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ + PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Lo,/* PCI below 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Hi,/* PCI above 1Mb */ + 0 /* end of table */ + }; + + +static void cy_start(struct tty_struct *); +static void set_line_char(struct cyclades_port *); +static void cy_probe(int, void *, struct pt_regs *); +static void cyz_poll(unsigned long); +#ifdef CY_SHOW_STATUS static void show_status(int); #endif +#if defined(CONFIG_PROC_FS) && !defined(MODULE) +static int cyclades_get_proc_info(char *, char **, off_t, int, int); +static struct proc_dir_entry cyclades_proc_entry = { + 0, + 8, + "cyclades", + S_IFREG | S_IRUGO, + 1, + 0, + 0, + 0, + 0, + cyclades_get_proc_info +}; +#endif + +/* The Cyclades-Z polling cycle is defined by this variable */ +static long cyz_polling_cycle = CZ_DEF_POLL; + +static int cyz_timeron = 0; +static struct timer_list +cyz_timerlist = { + NULL, NULL, 0, 0, cyz_poll +}; + +/************************************************** +error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long)); +copy_to_user (to, from, count); +*************************************************************** +error = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long *)); +copy_from_user(to, from, count); +**************************************************/ + static inline int serial_paranoia_check(struct cyclades_port *info, - kdev_t device, const char *routine) + kdev_t device, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = - "Warning: bad magic number for serial struct (%s) in %s\n"; + "cyc Warning: bad magic number for serial struct (%s) in %s\n"; static const char *badinfo = - "Warning: null cyclades_port for (%s) in %s\n"; + "cyc Warning: null cyclades_port for (%s) in %s\n"; static const char *badrange = - "Warning: cyclades_port out of range for (%s) in %s\n"; + "cyc Warning: cyclades_port out of range for (%s) in %s\n"; if (!info) { - printk(badinfo, kdevname(device), routine); - return 1; + printk(badinfo, kdevname(device), routine); + return 1; } if( (long)info < (long)(&cy_port[0]) || (long)(&cy_port[NR_PORTS]) < (long)info ){ - printk(badrange, kdevname(device), routine); - return 1; + printk(badrange, kdevname(device), routine); + return 1; } if (info->magic != CYCLADES_MAGIC) { - printk(badmagic, kdevname(device), routine); - return 1; + printk(badmagic, kdevname(device), routine); + return 1; } #endif - return 0; + return 0; } /* serial_paranoia_check */ + /* The following diagnostic routines allow the driver to spew information on the screen, even (especially!) during interrupts. */ -void +static void SP(char *data){ unsigned long flags; save_flags(flags); cli(); console_print(data); restore_flags(flags); -} -void +}/* SP */ + +static void CP(char data){ unsigned long flags; char scrn[2]; @@ -506,111 +866,17 @@ restore_flags(flags); }/* CP */ -void CP1(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP1 */ -void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */ -void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */ -void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */ - -/* This routine waits up to 1000 micro-seconds for the previous - command to the Cirrus chip to complete and then issues the - new command. An error is returned if the previous command - didn't finish within the time limit. - */ -u_short -write_cy_cmd(u_char *base_addr, u_char cmd, int index) -{ - unsigned long flags; - volatile int i; - - save_flags(flags); cli(); - /* Check to see that the previous command has completed */ - for(i = 0 ; i < 100 ; i++){ - if (base_addr[CyCCR<driver_data; - unsigned char *base_addr; - int chip,channel,index; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_stop ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_stop")) - return; - - cinfo = &cy_card[info->card]; - index = cinfo->bus_index; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - base_addr = (unsigned char*) - (cy_card[info->card].base_addr + (cy_chip_offset[chip]<driver_data; - unsigned char *base_addr; - int chip,channel,index; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_start ttyC%d\n", info->line); /* */ +static void CP4(int data) + { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */ +static void CP8(int data) + { CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */ +#if 0 +static void CP16(int data) + { CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */ +static void CP32(long data) + { CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */ #endif - if (serial_paranoia_check(info, tty->device, "cy_start")) - return; - - cinfo = &cy_card[info->card]; - index = cinfo->bus_index; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - base_addr = (unsigned char*) - (cy_card[info->card].base_addr + (cy_chip_offset[chip]<event |= 1 << event; /* remember what kind of event and who */ - queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */ + queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */ mark_bh(CYCLADES_BH); /* then trigger event */ } /* cy_sched_event */ -static int probe_ready; - /* - * This interrupt routine is used - * while we are probing for submarines. + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using cy_sched_event(), and they get done here. + * + * This is done through one level of indirection--the task queue. + * When a hardware interrupt service routine wants service by the + * driver's bottom half, it enqueues the appropriate tq_struct (one + * per port) to the tq_cyclades work queue and sets a request flag + * via mark_bh for processing that queue. When the time is right, + * do_cyclades_bh is called (because of the mark_bh) and it requests + * that the work queue be processed. + * + * Although this may seem unwieldy, it gives the system a way to + * pass an argument (in this case the pointer to the cyclades_port + * structure) to the bottom half of the driver. Previous kernels + * had to poll every port to see if that port needed servicing. */ static void -cy_probe(int irq, void *dev_id, struct pt_regs *regs) +do_cyclades_bh(void) { - int save_xir, save_car; - int index = 0; /* probing interrupts is only for ISA */ + run_task_queue(&tq_cyclades); +} /* do_cyclades_bh */ - if (!probe_ready) { - *(intr_base_addr + (Cy_ClrIntr<tty; + if (!tty) return; + + if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); } +#ifdef Z_WAKE + if (clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->shutdown_wait); + } +#endif +} /* do_softint */ - cy_irq_triggered = irq; - cy_triggered |= 1 << irq; - if(intr_base_addr[CySVRR<base_addr; - index = cinfo->bus_index; + /* Issue the new command */ + cy_writeb((u_long)base_addr+(CyCCR<num_chips ; chip ++) { - base_addr = (unsigned char *) - (cinfo->base_addr + (cy_chip_offset[chip]<= jiffies) + ; + + cy_triggered = 0; /* Reset after letting things settle */ + + timeout = jiffies+(HZ/10); + while (timeout >= jiffies) + ; + + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { + if ((cy_triggered & (1 << i)) && + (irq_lines & (1 << i))) { + wild_interrupts |= mask; + } + } + free_all_interrupts(irq_lines); + restore_flags(flags); + return wild_interrupts; +} /* check_wild_interrupts */ + +/* + * This routine is called by do_auto_irq(); it attempts to determine + * which interrupt a serial port is configured to use. It is not + * fool-proof, but it works a large part of the time. + */ +static int +get_auto_irq(volatile ucchar *address) +{ + unsigned long timeout; + volatile ucchar *base_addr; + int index; + unsigned long flags; + + index = 0; /* IRQ probing is only for ISA */ + base_addr = address; + intr_base_addr = address; + + /* + * Enable interrupts and see who answers + */ + cy_irq_triggered = 0; + save_flags(flags); cli(); + cy_writeb((u_long)base_addr+(CyCAR<= jiffies) { + if (cy_irq_triggered) + break; + } + probe_ready = 0; + return(cy_irq_triggered); +} /* get_auto_irq */ + +/* + * Calls get_auto_irq() multiple times, to make sure we don't get + * faked out by random interrupts + */ +static int +do_auto_irq(volatile ucchar *address) +{ + int irq_lines = 0; + int irq_try_1 = 0, irq_try_2 = 0; + int retries; + unsigned long flags; + + /* Turn on interrupts (they may be off) */ + save_flags(flags); sti(); + + probe_ready = 0; + + cy_wild_int_mask = check_wild_interrupts(); + + irq_lines = grab_all_interrupts(cy_wild_int_mask); + + for (retries = 0; retries < 5; retries++) { + if (!irq_try_1) + irq_try_1 = get_auto_irq(address); + if (!irq_try_2) + irq_try_2 = get_auto_irq(address); + if (irq_try_1 && irq_try_2) { + if (irq_try_1 == irq_try_2) + break; + irq_try_1 = irq_try_2 = 0; + } + } + restore_flags(flags); + free_all_interrupts(irq_lines); + return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; +} /* do_auto_irq */ + + +/* + * This interrupt routine is used + * while we are probing for submarines. + */ +static void +cy_probe(int irq, void *dev_id, struct pt_regs *regs) +{ + int save_xir, save_car; + int index = 0; /* probing interrupts is only for ISA */ + + if (!probe_ready) { + cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<base_addr; + index = cinfo->bus_index; + + + /* This loop checks all chips in the card. Make a note whenever + _any_ chip had some work to do, as this is considered an + indication that there will be more to do. Only when no chip + has any work does this outermost loop exit. + */ + do{ + had_work = 0; + for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) { + base_addr = (unsigned char *) + (cinfo->base_addr + (cy_chip_offset[chip]<first_line; info = &cy_port[i]; info->last_active = jiffies; - save_car = base_addr[CyCAR<tty == 0){ - j = (base_addr[CyRIVR<tty; - j = (base_addr[CyRIVR<ignore_status_mask){ continue; } if (tty->flip.count < TTY_FLIPBUF_SIZE){ - tty->flip.count++; - if (data & info->read_status_mask){ - if(data & CyBREAK){ - *tty->flip.flag_buf_ptr++ = - TTY_BREAK; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flags & ASYNC_SAK){ - do_SAK(tty); - } - }else if(data & CyFRAME){ - *tty->flip.flag_buf_ptr++ = - TTY_FRAME; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = - TTY_PARITY; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = - TTY_OVERRUN; - *tty->flip.char_buf_ptr++ = 0; - /* If the flip buffer itself is - overflowing, we still loose - the next incoming character. - */ - if(tty->flip.count < TTY_FLIPBUF_SIZE){ - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = - TTY_NORMAL; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = 0; - *tty->flip.char_buf_ptr++ = 0; - } - }else{ - *tty->flip.flag_buf_ptr++ = 0; - *tty->flip.char_buf_ptr++ = 0; - } + tty->flip.count++; + if (data & info->read_status_mask){ + if(data & CyBREAK){ + *tty->flip.flag_buf_ptr++ = + TTY_BREAK; + *tty->flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<flags & ASYNC_SAK){ + do_SAK(tty); + } + }else if(data & CyFRAME){ + *tty->flip.flag_buf_ptr++ = + TTY_FRAME; + *tty->flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<idle_stats.frame_errs++; + }else if(data & CyPARITY){ + *tty->flip.flag_buf_ptr++ = + TTY_PARITY; + *tty->flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<idle_stats.parity_errs++; + }else if(data & CyOVERRUN){ + *tty->flip.flag_buf_ptr++ = + TTY_OVERRUN; + *tty->flip.char_buf_ptr++ = 0; + /* If the flip buffer itself is + overflowing, we still loose + the next incoming character. + */ + if(tty->flip.count + < TTY_FLIPBUF_SIZE){ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = + TTY_NORMAL; + *tty->flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<idle_stats.overruns++; + /* These two conditions may imply */ + /* a normal read should be done. */ + /* }else if(data & CyTIMEOUT){ */ + /* }else if(data & CySPECHAR){ */ + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } }else{ - /* there was a software buffer overrun - and nothing could be done about it!!! */ + /* there was a software buffer + overrun and nothing could be + done about it!!! */ + info->idle_stats.overruns++; } } else { /* normal character reception */ - /* load # characters available from the chip */ - char_count = base_addr[CyRDCR<mon.int_count; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; +#ifdef CY_ENABLE_MONITORING + ++info->mon.int_count; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; #endif + info->idle_stats.recv_bytes += char_count; + info->idle_stats.recv_idle = jiffies; + while(char_count--){ - if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ break; } - tty->flip.count++; - data = base_addr[CyRDSR<flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = data; -#ifdef CYCLOM_16Y_HACK - udelay(10L); + tty->flip.count++; + data = cy_readb(base_addr+(CyRDSR<flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; +#ifdef CY_16Y_HACK + udelay(10L); #endif } } - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + queue_task(&tty->flip.tqueue, &tq_timer); } /* end of service */ - base_addr[CyRIR<first_line; - save_car = base_addr[CyCAR<last_active = jiffies; if(info->tty == 0){ - base_addr[CySRER<xmit_fifo_size; if(info->x_char) { /* send special char */ outch = info->x_char; - base_addr[CyTDR<x_char = 0; } - if (info->x_break){ - /* The Cirrus chip requires the "Embedded Transmit - Commands" of start break, delay, and end break - sequences to be sent. The duration of the - break is given in TICs, which runs at HZ - (typically 100) and the PPR runs at 200 Hz, - so the delay is duration * 200/HZ, and thus a - break can run from 1/100 sec to about 5/4 sec. - */ - base_addr[CyTDR<x_break*200/HZ; - base_addr[CyTDR<x_break = 0; - } + if (info->x_break){ + /* The Cirrus chip requires the "Embedded + Transmit Commands" of start break, delay, + and end break sequences to be sent. The + duration of the break is given in TICs, + which runs at HZ (typically 100) and the + PPR runs at 200 Hz, so the delay is + duration * 200/HZ, and thus a break can + run from 1/100 sec to about 5/4 sec. + For CD1400 J or later, replace the 200 Hz + by 500 Hz. + */ + /* start break */ + cy_writeb((u_long)base_addr + (CyTDR<chip_rev >= CD1400_REV_J ) { + /* It is a CD1400 rev. J or later */ + cy_writeb((u_long)base_addr + (CyTDR<x_break*500/HZ); + } else { + cy_writeb((u_long)base_addr + (CyTDR<x_break*200/HZ); + } + /* finish break */ + cy_writeb((u_long)base_addr + (CyTDR<x_break = 0; + } while (char_count-- > 0){ - if (!info->xmit_cnt){ - base_addr[CySRER<xmit_cnt){ + cy_writeb((u_long)base_addr+(CySRER<xmit_buf == 0){ - base_addr[CySRER<tty->stopped || info->tty->hw_stopped){ - base_addr[CySRER<xmit_buf[info->xmit_tail]; if( outch ){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (SERIAL_XMIT_SIZE - 1); + cy_writeb((u_long)base_addr+(CyTDR< 1){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (SERIAL_XMIT_SIZE - 1); + cy_writeb((u_long)base_addr+(CyTDR<xmit_cnt < WAKEUP_CHARS) { cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } - txend: /* end of service */ - base_addr[CyTIR<first_line]; + info = &cy_port[channel + chip * 4 + + cinfo->first_line]; info->last_active = jiffies; - save_car = base_addr[CyCAR<tty == 0){ /* nowhere to put the data, ignore it */ + if(info->tty == 0){/* no place for data, ignore it*/ ; }else{ if((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)){ if(mdm_status & CyDCD){ - cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); - }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) - &&(info->flags & ASYNC_CALLOUT_NOHUP))){ - cy_sched_event(info, Cy_EVENT_HANGUP); + cy_sched_event(info, + Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags + & ASYNC_CALLOUT_ACTIVE) + &&(info->flags + & ASYNC_CALLOUT_NOHUP))){ + cy_sched_event(info, + Cy_EVENT_HANGUP); } } if((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)){ if(info->tty->hw_stopped){ if(mdm_status & CyCTS){ - /* !!! cy_start isn't used because... */ + /* cy_start isn't used + because... !!! */ info->tty->hw_stopped = 0; - base_addr[CySRER<tty->hw_stopped = 1; - base_addr[CySRER<tty; - if (!tty) - return; - - if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~(ASYNC_NORMAL_ACTIVE| - ASYNC_CALLOUT_ACTIVE); - } - if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { - wake_up_interruptible(&info->open_wait); - } - if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + unsigned long loc_doorbell; + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)){ + return (-1); + } + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + + loc_doorbell = cy_readl(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->loc_doorbell); + if (loc_doorbell){ + *cmd = (char)(0xff & loc_doorbell); + *channel = cy_readl(&board_ctrl->fwcmd_channel); + *param = (uclong)cy_readl(&board_ctrl->fwcmd_param); + cy_writel(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->loc_doorbell, + 0xffffffff); + return 1; } -} /* do_softint */ + return 0; +} /* cyz_fetch_msg */ -/* - * Grab all interrupts in preparation for doing an automatic irq - * detection. dontgrab is a mask of irq's _not_ to grab. Returns a - * mask of irq's which were grabbed and should therefore be freed - * using free_all_interrupts(). - */ static int -grab_all_interrupts(int dontgrab) +cyz_issue_cmd( struct cyclades_card *cinfo, + uclong channel, ucchar cmd, uclong param) { - int irq_lines = 0; - int i, mask; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if (!(mask & dontgrab) - && !request_irq(i, cy_probe, SA_INTERRUPT, "serial probe", NULL)) { - irq_lines |= mask; - } + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + volatile uclong *pci_doorbell; + int index; + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)){ + return (-1); + } + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + + index = 0; + pci_doorbell = (uclong *)(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->pci_doorbell); + while( (cy_readl(pci_doorbell) & 0xff) != 0){ + if (index++ == 1000){ + return(-1); + } + udelay(50L); } - return irq_lines; -} /* grab_all_interrupts */ + cy_writel((u_long)&board_ctrl->hcmd_channel, channel); + cy_writel((u_long)&board_ctrl->hcmd_param , param); + cy_writel((u_long)pci_doorbell, (long)cmd); + + return(0); +} /* cyz_issue_cmd */ + + +#if 0 +static int +cyz_update_channel( struct cyclades_card *cinfo, + u_long channel, u_char mode, u_char cmd) +{ + struct FIRM_ID *firm_id = + (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + struct ZFW_CTRL *zfw_ctrl; + struct CH_CTRL *ch_ctrl; + + if (!ISZLOADED(*cinfo)){ + return (-1); + } + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + ch_ctrl = zfw_ctrl->ch_ctrl; + + cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode); + + return cyz_issue_cmd(cinfo, channel, cmd, 0L); + +} /* cyz_update_channel */ +#endif + -/* - * Release all interrupts grabbed by grab_all_interrupts - */ static void -free_all_interrupts(int irq_lines) +cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int i; - - for (i = 0; i < 16; i++) { - if (irq_lines & (1 << i)) - free_irq(i,NULL); - } -} /* free_all_interrupts */ +} /* cyz_interrupt */ -/* - * This routine returns a bitfield of "wild interrupts". Basically, - * any unclaimed interrupts which is flapping around. - */ -static int -check_wild_interrupts(void) + +static void +cyz_poll(unsigned long arg) { - int i, mask; - int wild_interrupts = 0; - int irq_lines; - unsigned long timeout; - unsigned long flags; - - /*Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct BOARD_CTRL *board_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; + struct cyclades_card *cinfo; + struct cyclades_port *info; + struct tty_struct *tty; + int card, port; + int char_count; +#ifdef BLOCKMOVE + int small_count; +#endif + char data; + uclong channel; + ucchar cmd; + uclong param; + uclong hw_ver, fw_ver; + volatile uclong tx_put, tx_get, tx_bufsize; + volatile uclong rx_put, rx_get, rx_bufsize; + + cyz_timerlist.expires = jiffies + (HZ); + for (card = 0 ; card < NR_CARDS ; card++){ + cinfo = &cy_card[card]; + if (!IS_CYC_Z(*cinfo)) continue; + + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)) { + cinfo->inact_ctrl = 0; + continue; + } - irq_lines = grab_all_interrupts(0); - - /* - * Delay for 0.1 seconds -- we use a busy loop since this may - * occur during the bootup sequence - */ - timeout = jiffies+10; - while (timeout >= jiffies) - ; - - cy_triggered = 0; /* Reset after letting things settle */ - - timeout = jiffies+10; - while (timeout >= jiffies) - ; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if ((cy_triggered & (1 << i)) && - (irq_lines & (1 << i))) { - wild_interrupts |= mask; + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &(zfw_ctrl->board_ctrl); + fw_ver = cy_readl(&board_ctrl->fw_version); + hw_ver = cy_readl(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->mail_box_0); + + /* Enables the firmware inactivity control */ + if ((fw_ver > 0x00000310L) && (!cinfo->inact_ctrl)) { + param = cyz_issue_cmd( &cy_card[card], 0L, C_CM_TINACT, 0L); + cinfo->inact_ctrl = 1; + } + + while(cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1){ + char_count = 0; + info = &cy_port[ channel + cinfo->first_line ]; + if((tty = info->tty) == 0) continue; + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + info->jiffies[0] = jiffies; + + switch(cmd){ + case C_CM_PR_ERROR: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_FR_ERROR: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_RXBRK: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_MDCD: + if (info->flags & ASYNC_CHECK_CD){ + if ((fw_ver > 241 ? + ((u_long)param) : + cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) { + /* SP("Open Wakeup\n"); */ + cy_sched_event(info, + Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags + & ASYNC_CALLOUT_ACTIVE) + &&(info->flags + & ASYNC_CALLOUT_NOHUP))){ + /* SP("Hangup\n"); */ + cy_sched_event(info, + Cy_EVENT_HANGUP); + } + } + break; + case C_CM_MCTS: + if (info->flags & ASYNC_CTS_FLOW) { + if(info->tty->hw_stopped){ + if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){ + /* cy_start isn't used because... + HW flow is handled by the board */ + /* SP("Write Wakeup\n"); */ + cy_sched_event(info, + Cy_EVENT_WRITE_WAKEUP); + } + }else{ + if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){ + /* cy_stop isn't used because + HW flow is handled by the board */ + /* SP("Write stop\n"); */ + } + } + } + break; + case C_CM_MRI: + break; + case C_CM_MDSR: + break; +#ifdef Z_WAKE + case C_CM_IOCTLW: + cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP); + break; +#endif + case C_CM_FATAL: + /* should do something with this !!! */ + break; + } + if(char_count){ + queue_task(&tty->flip.tqueue, &tq_timer); } } - free_all_interrupts(irq_lines); - restore_flags(flags); - return wild_interrupts; -} /* check_wild_interrupts */ + for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){ + info = &cy_port[ port + cinfo->first_line ]; + tty = info->tty; + ch_ctrl = &(zfw_ctrl->ch_ctrl[port]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); + +/* Removed due to compilation problems in Alpha systems */ +// if ((char_count = CHARS_IN_BUF(buf_ctrl))){ + + rx_get = cy_readl(&buf_ctrl->rx_get); + rx_put = cy_readl(&buf_ctrl->rx_put); + rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize); + if (rx_put >= rx_get) + char_count = rx_put - rx_get; + else + char_count = rx_put - rx_get + rx_bufsize; + + if ( char_count ){ + + info->last_active = jiffies; + info->jiffies[1] = jiffies; + +#ifdef CY_ENABLE_MONITORING + info->mon.int_count++; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + info->idle_stats.recv_bytes += char_count; + info->idle_stats.recv_idle = jiffies; + if( tty == 0){ + /* flush received characters */ + rx_get = (rx_get + char_count) & (rx_bufsize - 1); + /* SP("-"); */ + info->rflush_count++; + }else{ +#ifdef BLOCKMOVE + /* we'd like to use memcpy(t, f, n) and memset(s, c, count) + for performance, but because of buffer boundaries, there + may be several steps to the operation */ + while(0 < (small_count + = cy_min((rx_bufsize - rx_get), + cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), + char_count)))){ + + memcpy_fromio(tty->flip.char_buf_ptr, + (char *)(cinfo->base_addr + + cy_readl(&buf_ctrl->rx_bufaddr) + + rx_get), + small_count); + + tty->flip.char_buf_ptr += small_count; + memset(tty->flip.flag_buf_ptr, + TTY_NORMAL, + small_count); + tty->flip.flag_buf_ptr += small_count; + rx_get = (rx_get + small_count) & (rx_bufsize - 1); + char_count -= small_count; + tty->flip.count += small_count; + } +#else + while(char_count--){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + break; + } + data = cy_readb(cinfo->base_addr + + cy_readl(&buf_ctrl->rx_bufaddr) + rx_get); + rx_get = (rx_get + 1) & (rx_bufsize - 1); + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; + } +#endif + queue_task(&tty->flip.tqueue, &tq_timer); + } + /* Update rx_get */ + cy_writel(&buf_ctrl->rx_get, rx_get); + } -/* - * This routine is called by do_auto_irq(); it attempts to determine - * which interrupt a serial port is configured to use. It is not - * fool-proof, but it works a large part of the time. - */ -static int -get_auto_irq(unsigned char *address) -{ - unsigned long timeout; - unsigned char *base_addr; - int index; +/* Removed due to compilation problems in Alpha systems */ +// if ((char_count = SPACE_IN_BUF(buf_ctrl))){ - index = 0; /* IRQ probing is only for ISA */ - base_addr = address; - intr_base_addr = address; - - /* - * Enable interrupts and see who answers - */ - cy_irq_triggered = 0; - cli(); - base_addr[CyCAR<= jiffies) { - if (cy_irq_triggered) - break; - } - probe_ready = 0; - return(cy_irq_triggered); -} /* get_auto_irq */ + tx_get = cy_readl(&buf_ctrl->tx_get); + tx_put = cy_readl(&buf_ctrl->tx_put); + tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); + if (tx_put >= tx_get) + char_count = tx_get - tx_put - 1 + tx_bufsize; + else + char_count = tx_get - tx_put - 1; -/* - * Calls get_auto_irq() multiple times, to make sure we don't get - * faked out by random interrupts - */ -static int -do_auto_irq(unsigned char *address) -{ - int irq_lines = 0; - int irq_try_1 = 0, irq_try_2 = 0; - int retries; - unsigned long flags; + if ( char_count ){ - /* Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + if( tty == 0 ){ + goto ztxdone; + } - probe_ready = 0; + if(info->x_char) { /* send special char */ + data = info->x_char; - cy_wild_int_mask = check_wild_interrupts(); + cy_writeb((cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + info->x_char = 0; + char_count--; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } + if (info->x_break){ + printk("cyc cyz_poll shouldn't see x_break\n"); + info->x_break = 0; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#ifdef BLOCKMOVE + while(0 < (small_count + = cy_min((tx_bufsize - tx_put), + cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail), + cy_min(info->xmit_cnt, char_count))))){ + + memcpy_toio((char *)(cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), + &info->xmit_buf[info->xmit_tail], + small_count); + + tx_put = (tx_put + small_count) & (tx_bufsize - 1); + char_count -= small_count; + info->xmit_cnt -= small_count; + info->xmit_tail = + (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1); + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#else + while (info->xmit_cnt && char_count){ + data = info->xmit_buf[info->xmit_tail]; + info->xmit_cnt--; + info->xmit_tail = + (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); + + cy_writeb(cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, + data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + char_count--; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } - irq_lines = grab_all_interrupts(cy_wild_int_mask); - - for (retries = 0; retries < 5; retries++) { - if (!irq_try_1) - irq_try_1 = get_auto_irq(address); - if (!irq_try_2) - irq_try_2 = get_auto_irq(address); - if (irq_try_1 && irq_try_2) { - if (irq_try_1 == irq_try_2) - break; - irq_try_1 = irq_try_2 = 0; +#endif + ztxdone: + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + /* Update tx_put */ + cy_writel(&buf_ctrl->tx_put, tx_put); } } - restore_flags(flags); - free_all_interrupts(irq_lines); - return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; -} /* do_auto_irq */ + /* poll every 40 ms */ + cyz_timerlist.expires = jiffies + cyz_polling_cycle; + + /* refresh inactivity counter */ + if (cinfo->inact_ctrl) { + cy_writel(&board_ctrl->inactivity, (uclong) ZF_TINACT); + } + } + add_timer(&cyz_timerlist); + + return; +} /* cyz_poll */ + + +/********** End of block of Cyclades-Z specific code *********/ +/***********************************************************/ /* This is called whenever a port becomes active; @@ -1216,72 +1992,144 @@ int card,chip,channel,index; if (info->flags & ASYNC_INITIALIZED){ - return 0; + return 0; } if (!info->type){ - if (info->tty){ - set_bit(TTY_IO_ERROR, &info->tty->flags); - } - return 0; + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + return 0; } if (!info->xmit_buf){ - info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); - if (!info->xmit_buf){ - return -ENOMEM; - } + info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); + if (!info->xmit_buf){ + return -ENOMEM; + } } - config_setup(info); + set_line_char(info); card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) (cy_card[card].base_addr + (cy_chip_offset[chip]<default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + cy_writeb((ulong)base_addr+(CyRTPR<default_timeout + ? info->default_timeout + : 0x02)); /* 10ms rx timeout */ - write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); - base_addr[CyCAR<flags |= ASYNC_INITIALIZED; + cy_writeb((u_long)base_addr+(CySRER<flags |= ASYNC_INITIALIZED; + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (unsigned char*) (cy_card[card].base_addr); + + firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + if (!ISZLOADED(cy_card[card])){ + return -ENODEV; + } + + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + +#ifdef CY_DEBUG_OPEN + printk("cyc startup Z card %d, channel %d, base_addr %lx\n", + card, channel, (long)base_addr);/**/ +#endif + + cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE); +#ifdef Z_WAKE + cy_writel(&ch_ctrl[channel].intr_enable, + C_IN_MDCD|C_IN_MCTS|C_IN_IOCTLW); +#else + cy_writel(&ch_ctrl[channel].intr_enable, + C_IN_MDCD|C_IN_MCTS); +#endif + retval = cyz_issue_cmd( &cy_card[card], + channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */ + if (retval != 0){ + printk("cyc:startup(1) retval was %x\n", retval); + } + + /* set timeout !!! */ + /* set RTS and DTR !!! */ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR) ; + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:startup(2) retval was %x\n", retval); + } +#ifdef CY_DEBUG_DTR + printk("cyc:startup raising Z DTR\n"); +#endif + /* enable send, recv, modem !!! */ + + info->flags |= ASYNC_INITIALIZED; if (info->tty){ clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; + } - restore_flags(flags); - -#ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); +#ifdef CY_DEBUG_OPEN + printk(" cyc startup done\n"); #endif - return 0; + return 0; } /* startup */ -void + +static void start_xmit( struct cyclades_port *info ) { unsigned long flags; @@ -1290,18 +2138,25 @@ card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<flags & ASYNC_INITIALIZED)){ - return; + return; } card = info->card; channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<xmit_buf){ - unsigned char * temp; - temp = info->xmit_buf; - info->xmit_buf = 0; - free_page((unsigned long) temp); - } - - base_addr[CyCAR<tty || (info->tty->termios->c_cflag & HUPCL)) { - base_addr[CyMSVR1<tty){ - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->xmit_buf){ + unsigned char * temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); + } + cy_writeb((u_long)base_addr+(CyCAR<tty || (info->tty->termios->c_cflag & HUPCL)) { + cy_writeb((u_long)base_addr+(CyMSVR1<tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (unsigned char*) (cy_card[card].base_addr); +#ifdef CY_DEBUG_OPEN + printk("cyc shutdown Z card %d, channel %d, base_addr %lx\n", + card, channel, (long)base_addr); +#endif + + firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + if (!ISZLOADED(cy_card[card])) { + return; } - info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); -#ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &(zfw_ctrl->board_ctrl); + ch_ctrl = zfw_ctrl->ch_ctrl; + + save_flags(flags); cli(); + + if (info->xmit_buf){ + unsigned char * temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); + } + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + cy_writel((u_long)&ch_ctrl[channel].rs_control, + (uclong)(cy_readl(&ch_ctrl[channel].rs_control) & + ~(C_RS_RTS | C_RS_DTR))); + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:shutdown retval (2) was %x\n", retval); + } +#ifdef CY_DEBUG_DTR + printk("cyc:shutdown dropping Z DTR\n"); +#endif + } + + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + + restore_flags(flags); + } + +#ifdef CY_DEBUG_OPEN + printk(" cyc shutdown done\n"); #endif return; } /* shutdown */ + /* - * This routine finds or computes the various line characteristics. + * ------------------------------------------------------------ + * cy_open() and friends + * ------------------------------------------------------------ */ -static void -config_setup(struct cyclades_port * info) + +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct cyclades_port *info) { + struct wait_queue wait = { current, NULL }; + struct cyclades_card *cinfo; unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - unsigned cflag; - int i; - - if (!info->tty || !info->tty->termios){ - return; - } - if (info->line == -1){ - return; - } - cflag = info->tty->termios->c_cflag; + int chip, channel,index; + int retval; + char *base_addr; - /* baud rate */ - i = cflag & CBAUD; -#ifdef CBAUDEX -/* Starting with kernel 1.1.65, there is direct support for - higher baud rates. The following code supports those - changes. The conditional aspect allows this driver to be - used for earlier as well as later kernel versions. (The - mapping is slightly different from serial.c because there - is still the possibility of supporting 75 kbit/sec with - the Cyclades board.) - */ - if (i & CBAUDEX) { - if (i == B57600) - i = 16; - else if(i == B115200) - i = 18; -#ifdef B78600 - else if(i == B78600) - i = 17; -#endif - else - info->tty->termios->c_cflag &= ~CBAUDEX; - } -#endif - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 3; - } - info->tbpr = baud_bpr[i]; /* Tx BPR */ - info->tco = baud_co[i]; /* Tx CO */ - info->rbpr = baud_bpr[i]; /* Rx BPR */ - info->rco = baud_co[i]; /* Rx CO */ - if (baud_table[i] == 134) { - info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; - /* get it right for 134.5 baud */ - } else if (baud_table[i]) { - info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - /* By tradition (is it a standard?) a baud rate of zero - implies the line should be/has been closed. A bit - later in this routine such a test is performed. */ - - /* byte size and parity */ - info->cor5 = 0; - info->cor4 = 0; - info->cor3 = (info->default_threshold - ? info->default_threshold - : baud_cor3[i]); /* receive threshold */ - info->cor2 = CyETC; - switch(cflag & CSIZE){ - case CS5: - info->cor1 = Cy_5_BITS; - break; - case CS6: - info->cor1 = Cy_6_BITS; - break; - case CS7: - info->cor1 = Cy_7_BITS; - break; - case CS8: - info->cor1 = Cy_8_BITS; - break; - } - if(cflag & CSTOPB){ - info->cor1 |= Cy_2_STOP; - } - if (cflag & PARENB){ - if (cflag & PARODD){ - info->cor1 |= CyPARITY_O; + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&info->close_wait); + } + if (info->flags & ASYNC_HUP_NOTIFY){ + return -EAGAIN; }else{ - info->cor1 |= CyPARITY_E; + return -ERESTARTSYS; } - }else{ - info->cor1 |= CyPARITY_NONE; } - - /* CTS flow control flag */ - if (cflag & CRTSCTS){ - info->flags |= ASYNC_CTS_FLOW; - info->cor2 |= CyCtsAE; - }else{ - info->flags &= ~ASYNC_CTS_FLOW; - info->cor2 &= ~CyCtsAE; + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)){ + return -EBUSY; + } + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; } - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else - info->flags |= ASYNC_CHECK_CD; - - /*********************************************** - The hardware option, CyRtsAO, presents RTS when - the chip has characters to send. Since most modems - use RTS as reverse (inbound) flow control, this - option is not used. If inbound flow control is - necessary, DTR can be programmed to provide the - appropriate signals for use with a non-standard - cable. Contact Marcio Saito for details. - ***********************************************/ - card = info->card; - channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE){ + return -EBUSY; + } + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * cy_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef CY_DEBUG_OPEN + printk("cyc block_til_ready before block: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif save_flags(flags); cli(); - base_addr[CyCAR<count--; + restore_flags(flags); +#ifdef CY_DEBUG_COUNT + printk("cyc block_til_ready: (%d): decrementing count to %d\n", + current->pid, info->count); +#endif + info->blocked_open++; - /* tx and rx baud rate */ + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + index = cinfo->bus_index; + base_addr = (char *)(cinfo->base_addr + + (cy_chip_offset[chip]<tco; - base_addr[CyTBPR<tbpr; - base_addr[CyRCOR<rco; - base_addr[CyRBPR<rbpr; - - /* set line characteristics according configuration */ - - base_addr[CySCHR1<tty); - base_addr[CySCHR2<tty); - base_addr[CyCOR1<cor1; - base_addr[CyCOR2<cor2; - base_addr[CyCOR3<cor3; - base_addr[CyCOR4<cor4; - base_addr[CyCOR5<cor5; - - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); - - base_addr[CyCAR<default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ - - if (C_CLOCAL(info->tty)) { - base_addr[CySRER<0 modem transitions */ - base_addr[CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<0 modem transitions */ - base_addr[CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<flags & ASYNC_CALLOUT_ACTIVE)){ + cy_writeb((u_long)base_addr+(CyCAR<state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + save_flags(flags); cli(); + cy_writeb((u_long)base_addr+(CyCAR<flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (cy_readb(base_addr+(CyMSVR1<signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } +#ifdef CY_DEBUG_OPEN + printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + schedule(); + } + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (char *)(cinfo->base_addr); + firm_id = (struct FIRM_ID *) + (base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)){ + return -EINVAL; } - if(i == 0){ /* baud rate is zero, turn off line */ - base_addr[CyMSVR2<zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + while (1) { + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR); + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:block_til_ready retval was %x\n", retval); + } +#ifdef CY_DEBUG_DTR + printk("cyc:block_til_ready raising Z DTR\n"); #endif - }else{ - base_addr[CyMSVR2<state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) { + break; + } + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } +#ifdef CY_DEBUG_OPEN + printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ #endif + schedule(); } + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)){ + info->count++; +#ifdef CY_DEBUG_COUNT + printk("cyc:block_til_ready (%d): incrementing count to %d\n", + current->pid, info->count); +#endif + } + info->blocked_open--; +#ifdef CY_DEBUG_OPEN + printk("cyc:block_til_ready after blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} /* block_til_ready */ - if (info->tty){ - clear_bit(TTY_IO_ERROR, &info->tty->flags); +/* + * This routine is called whenever a serial port is opened. It + * performs the serial-specific initialization for the tty structure. + */ +int +cy_open(struct tty_struct *tty, struct file * filp) +{ + struct cyclades_port *info; + int retval, line; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (NR_PORTS <= line)){ + return -ENODEV; + } + info = &cy_port[line]; + if (info->line < 0){ + return -ENODEV; + } + + /* If the card's firmware hasn't been loaded, + treat it as absent from the system. This + will make the user pay attention. + */ + if (IS_CYC_Z(cy_card[info->card])) { + if (!ISZLOADED(cy_card[info->card])) { + if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *) + ((cy_card[info->card]).ctl_addr))->mail_box_0)) && + Z_FPGA_CHECK(cy_card[info->card])) && + (ZFIRM_HLT==cy_readl(&((struct FIRM_ID *) + ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))) + { + printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n"); + } else { + printk("Cyclades-Z firmware not yet loaded\n"); + } + return -ENODEV; } + } +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_open ttyC%d\n", info->line); /* */ +#endif + if (serial_paranoia_check(info, tty->device, "cy_open")){ + return -ENODEV; + } +#ifdef CY_DEBUG_OPEN + printk("cyc:cy_open ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + info->count++; +#ifdef CY_DEBUG_COUNT + printk("cyc:cy_open (%d): incrementing count to %d\n", + current->pid, info->count); +#endif + tty->driver_data = info; + info->tty = tty; - restore_flags(flags); + /* Some drivers have (incorrect/incomplete) code to test + against a race condition. Should add good code here!!! */ + if (!tmp_buf) { + tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!tmp_buf){ + return -ENOMEM; + } + } + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + } + /* + * Start up serial port + */ + retval = startup(info); + if (retval){ + return retval; + } -} /* config_setup */ + MOD_INC_USE_COUNT; + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef CY_DEBUG_OPEN + printk("cyc:cy_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } -static void -cy_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; + info->session = current->session; + info->pgrp = current->pgrp; -#ifdef SERIAL_DEBUG_IO - printk("cy_put_char ttyC%d\n", info->line); +#ifdef CY_DEBUG_OPEN + printk(" cyc:cy_open done\n");/**/ #endif - if (serial_paranoia_check(info, tty->device, "cy_put_char")) - return; + return 0; +} /* cy_open */ - if (!tty || !info->xmit_buf) +/* + * cy_wait_until_sent() --- wait until the transmitter is empty + */ +static void cy_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + unsigned char *base_addr; + int card,chip,channel,index; + unsigned long orig_jiffies, char_time; + + if (serial_paranoia_check(info, tty->device, "cy_wait_until_sent")) return; - save_flags(flags); cli(); - if (info->xmit_cnt >= PAGE_SIZE - 1) { - restore_flags(flags); - return; + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout < 0) + timeout = 0; + if (timeout) + char_time = MIN(char_time, timeout); +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk("In cy_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + card = info->card; + channel = (info->line) - (cy_card[card].first_line); + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char *) + (cy_card[card].base_addr + (cy_chip_offset[chip]<state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + char_time; + schedule(); + if (current->signal & ~current->blocked) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; } + current->state = TASK_RUNNING; + } else { + // Nothing to do! + } + /* Run one more char cycle */ + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + (char_time * 5); + schedule(); + current->state = TASK_RUNNING; +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk("Clean (jiff=%lu)...done\n", jiffies); +#endif +} - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= PAGE_SIZE - 1; - info->xmit_cnt++; - restore_flags(flags); -} /* cy_put_char */ - - +/* + * This routine is called when a particular tty device is closed. + */ static void -cy_flush_chars(struct tty_struct *tty) +cy_close(struct tty_struct * tty, struct file * filp) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_chars ttyC%d\n", info->line); /* */ + +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_close ttyC%d\n", info->line); #endif - if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) - return; + if (!info + || serial_paranoia_check(info, tty->device, "cy_close")){ + return; + } +#ifdef CY_DEBUG_OPEN + printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count); +#endif - if (info->xmit_cnt <= 0 || tty->stopped - || tty->hw_stopped || !info->xmit_buf) - return; + save_flags(flags); cli(); - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("cyc:cy_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } +#ifdef CY_DEBUG_COUNT + printk("cyc:cy_close at (%d): decrementing count to %d\n", + current->pid, info->count - 1); +#endif + if (--info->count < 0) { +#ifdef CY_DEBUG_COUNT + printk("cyc:cyc_close setting count to 0\n"); +#endif + info->count = 0; + } + if (info->count) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; - save_flags(flags); cli(); - base_addr[CyCAR<closing = 1; + if (info->closing_wait != CY_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + + if (!IS_CYC_Z(cy_card[info->card])) { + int channel = info->line - cy_card[info->card].first_line; + int index = cy_card[info->card].bus_index; + unsigned char *base_addr = (unsigned char *) + (cy_card[info->card].base_addr + + (cy_chip_offset[channel>>2] <flags & ASYNC_INITIALIZED) { + /* Waiting for on-board buffers to be empty before closing + the port */ + cy_wait_until_sent(tty, info->timeout); + } + } else { +#ifdef Z_WAKE + /* Waiting for on-board buffers to be empty before closing the port */ + unsigned char *base_addr = (unsigned char *) + cy_card[info->card].base_addr; + struct FIRM_ID *firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + struct ZFW_CTRL *zfw_ctrl = + (struct ZFW_CTRL *) (base_addr + cy_readl(&firm_id->zfwctrl_addr)); + struct CH_CTRL *ch_ctrl = zfw_ctrl->ch_ctrl; + int channel = info->line - cy_card[info->card].first_line; + int retval; + + if (cy_readl(&ch_ctrl[channel].flow_status) != C_FS_TXIDLE) { + retval = cyz_issue_cmd(&cy_card[info->card], channel, + C_CM_IOCTLW, 0L); + if (retval != 0){ + printk("cyc:shutdown retval (1) was %x\n", retval); + } + interruptible_sleep_on(&info->shutdown_wait); + } +#endif + } + + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->close_delay; + schedule(); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + +#ifdef CY_DEBUG_OTHER + printk(" cyc:cy_close done\n"); +#endif + + MOD_DEC_USE_COUNT; restore_flags(flags); -} /* cy_flush_chars */ +} /* cy_close */ /* This routine gets called when tty_write has put something into - the write_queue. If the port is not already transmitting stuff, - start it off by enabling interrupts. The interrupt service - routine will then ensure that the characters are sent. If the - port is already active, there is no need to kick it. + * the write_queue. The characters may come from user space or + * kernel space. + * + * This routine will return the number of characters actually + * accepted for writing. + * + * If the port is not already transmitting stuff, start it off by + * enabling interrupts. The interrupt service routine will then + * ensure that the characters are sent. + * If the port is already active, there is no need to kick it. + * */ static int cy_write(struct tty_struct * tty, int from_user, @@ -1643,197 +2833,633 @@ unsigned long flags; int c, total = 0; -#ifdef SERIAL_DEBUG_IO - printk("cy_write ttyC%d\n", info->line); /* */ +#ifdef CY_DEBUG_IO + printk("cyc:cy_write ttyC%d\n", info->line); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_write")){ - return 0; + return 0; } - + if (!tty || !info->xmit_buf || !tmp_buf){ return 0; } + if (from_user) + down(&tmp_buf_sem); + save_flags(flags); while (1) { - save_flags(flags); cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0){ - restore_flags(flags); - break; - } + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; - if (from_user) { - down(&tmp_buf_sem); - memcpy_fromfs(tmp_buf, buf, c); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - up(&tmp_buf_sem); - } else - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - restore_flags(flags); - buf += c; - count -= c; - total += c; + if (from_user) { + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; +#if 0 + SP("CW"); + CP16(c); + SP(" "); +#endif } - - if (info->xmit_cnt - && !tty->stopped - && !tty->hw_stopped ) { + info->idle_stats.xmit_bytes += total; + info->idle_stats.xmit_idle = jiffies; + + if (from_user) + up(&tmp_buf_sem); + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { start_xmit(info); } + restore_flags(flags); return total; } /* cy_write */ -static int -cy_write_room(struct tty_struct *tty) +/* + * This routine is called by the kernel to write a single + * character to the tty device. If the kernel uses this routine, + * it must call the flush_chars() routine (if defined) when it is + * done stuffing characters into the driver. If there is no room + * in the queue, the character is ignored. + */ +static void +cy_put_char(struct tty_struct *tty, unsigned char ch) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - int ret; - -#ifdef SERIAL_DEBUG_IO - printk("cy_write_room ttyC%d\n", info->line); /* */ + unsigned long flags; + +#ifdef CY_DEBUG_IO + printk("cyc:cy_put_char ttyC%d\n", info->line); #endif - if (serial_paranoia_check(info, tty->device, "cy_write_room")) - return 0; - ret = PAGE_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} /* cy_write_room */ + if (serial_paranoia_check(info, tty->device, "cy_put_char")) + return; + if (!tty || !info->xmit_buf) + return; -static int -cy_chars_in_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - -#ifdef SERIAL_DEBUG_IO - printk("cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */ + save_flags(flags); cli(); + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE - 1; + info->xmit_cnt++; + info->idle_stats.xmit_bytes++; + info->idle_stats.xmit_idle = jiffies; + restore_flags(flags); +#if 0 + SP("+"); #endif - - if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer")) - return 0; - - return info->xmit_cnt; -} /* cy_chars_in_buffer */ +} /* cy_put_char */ +/* + * This routine is called by the kernel after it has written a + * series of characters to the tty device using put_char(). + */ static void -cy_flush_buffer(struct tty_struct *tty) +cy_flush_chars(struct tty_struct *tty) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_buffer ttyC%d\n", info->line); /* */ + unsigned char *base_addr; + int card,chip,channel,index; + +#ifdef CY_DEBUG_IO + printk("cyc:cy_flush_chars ttyC%d\n", info->line); /* */ #endif - if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) - return; - save_flags(flags); cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} /* cy_flush_buffer */ + if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) + return; + if (info->xmit_cnt <= 0 || tty->stopped + || tty->hw_stopped || !info->xmit_buf) + return; -/* This routine is called by the upper-layer tty layer to signal - that incoming characters should be throttled or that the - throttle should be released. + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<driver_data; - unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_throttle ttyC%d\n", info->line); + int ret; + +#ifdef CY_DEBUG_IO + printk("cyc:cy_write_room ttyC%d\n", info->line); /* */ #endif - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ - return; - } + if (serial_paranoia_check(info, tty->device, "cy_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} /* cy_write_room */ - if (I_IXOFF(tty)) { - info->x_char = STOP_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ - } + +static int +cy_chars_in_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + int card, channel; + + if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer")) + return 0; card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<line) - (cy_card[card].first_line); - save_flags(flags); cli(); - base_addr[CyCAR<line, info->xmit_cnt); /* */ +#endif + return info->xmit_cnt; + } else { + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; + int char_count; + volatile uclong tx_put, tx_get, tx_bufsize; + + firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS); + zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + + cy_readl(&firm_id->zfwctrl_addr)); + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + + tx_get = cy_readl(&buf_ctrl->tx_get); + tx_put = cy_readl(&buf_ctrl->tx_put); + tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); + if (tx_put >= tx_get) + char_count = tx_put - tx_get; + else + char_count = tx_put - tx_get + tx_bufsize; +#ifdef CY_DEBUG_IO + printk("cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt + char_count); /* */ +#endif + return (info->xmit_cnt + char_count); + } +} /* cy_chars_in_buffer */ - return; -} /* cy_throttle */ + +/* + * ------------------------------------------------------------ + * cy_ioctl() and friends + * ------------------------------------------------------------ + */ +/* + * This routine finds or computes the various line characteristics. + * It used to be called config_setup + */ static void -cy_unthrottle(struct tty_struct * tty) +set_line_char(struct cyclades_port * info) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; unsigned char *base_addr; int card,chip,channel,index; + unsigned cflag, iflag; + unsigned short chip_number; + int i; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_unthrottle ttyC%d\n", info->line); -#endif - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ - return; + if (!info->tty || !info->tty->termios){ + return; } - - if (I_IXOFF(tty)) { - info->x_char = START_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ + if (info->line == -1){ + return; } + cflag = info->tty->termios->c_cflag; + iflag = info->tty->termios->c_iflag; card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<line) - (cy_card[card].first_line); + chip_number = channel / 4; - save_flags(flags); cli(); - base_addr[CyCAR<chip_rev >= CD1400_REV_J)) { + /* It is a CD1400 rev. J or later */ + i = 20; + } + else + info->tty->termios->c_cflag &= ~CBAUDEX; + } + + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 3; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ + switch(info->baud) { + case 57600: + i += 1; break; +#ifdef B76800 + case 76800: + i += 2; break; +#endif + case 115200: + i += 3; break; + case 230400: + i += 5; break; + default: + break; + } + } + } + if(info->chip_rev >= CD1400_REV_J) { + /* It is a CD1400 rev. J or later */ + info->tbpr = baud_bpr_60[i]; /* Tx BPR */ + info->tco = baud_co_60[i]; /* Tx CO */ + info->rbpr = baud_bpr_60[i]; /* Rx BPR */ + info->rco = baud_co_60[i]; /* Rx CO */ + } else { + info->tbpr = baud_bpr_25[i]; /* Tx BPR */ + info->tco = baud_co_25[i]; /* Tx CO */ + info->rbpr = baud_bpr_25[i]; /* Rx BPR */ + info->rco = baud_co_25[i]; /* Rx CO */ + } + if (baud_table[i] == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (baud_table[i]) { + info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + /* By tradition (is it a standard?) a baud rate of zero + implies the line should be/has been closed. A bit + later in this routine such a test is performed. */ + + /* byte size and parity */ + info->cor5 = 0; + info->cor4 = 0; + info->cor3 = (info->default_threshold + ? info->default_threshold + : baud_cor3[i]); /* receive threshold */ + info->cor2 = CyETC; + switch(cflag & CSIZE){ + case CS5: + info->cor1 = Cy_5_BITS; + break; + case CS6: + info->cor1 = Cy_6_BITS; + break; + case CS7: + info->cor1 = Cy_7_BITS; + break; + case CS8: + info->cor1 = Cy_8_BITS; + break; + } + if(cflag & CSTOPB){ + info->cor1 |= Cy_2_STOP; + } + if (cflag & PARENB){ + if (cflag & PARODD){ + info->cor1 |= CyPARITY_O; + }else{ + info->cor1 |= CyPARITY_E; + } + }else{ + info->cor1 |= CyPARITY_NONE; + } + + /* CTS flow control flag */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + info->cor2 |= CyCtsAE; + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + info->cor2 &= ~CyCtsAE; + } + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /*********************************************** + The hardware option, CyRtsAO, presents RTS when + the chip has characters to send. Since most modems + use RTS as reverse (inbound) flow control, this + option is not used. If inbound flow control is + necessary, DTR can be programmed to provide the + appropriate signals for use with a non-standard + cable. Contact Marcio Saito for details. + ***********************************************/ + + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<tco); + cy_writeb((u_long)base_addr+(CyTBPR<tbpr); + cy_writeb((u_long)base_addr+(CyRCOR<rco); + cy_writeb((u_long)base_addr+(CyRBPR<rbpr); + + /* set line characteristics according configuration */ + + cy_writeb((u_long)base_addr+(CySCHR1<tty)); + cy_writeb((u_long)base_addr+(CySCHR2<tty)); + cy_writeb((u_long)base_addr+(CyCOR1<cor1); + cy_writeb((u_long)base_addr+(CyCOR2<cor2); + cy_writeb((u_long)base_addr+(CyCOR3<cor3); + cy_writeb((u_long)base_addr+(CyCOR4<cor4); + cy_writeb((u_long)base_addr+(CyCOR5<cor5); + + cyy_issue_cmd(base_addr, + CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); + + cy_writeb((u_long)base_addr+(CyCAR<default_timeout + ? info->default_timeout + : 0x02)); /* 10ms rx timeout */ + + if (C_CLOCAL(info->tty)) { + /* without modem intr */ + cy_writeb((u_long)base_addr+(CySRER<0 modem transitions */ + if ((cflag & CRTSCTS) && info->rflow) { + cy_writeb((u_long)base_addr+(CyMCOR1<1 modem transitions */ + cy_writeb((u_long)base_addr+(CyMCOR2<0 modem transitions */ + if ((cflag & CRTSCTS) && info->rflow) { + cy_writeb((u_long)base_addr+(CyMCOR1<1 modem transitions */ + cy_writeb((u_long)base_addr+(CyMCOR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + struct BUF_CTRL *buf_ctrl; + int retval; + + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (!ISZLOADED(cy_card[card])) { + return; + } + + zfw_ctrl = (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; + + /* baud rate */ + switch(i = cflag & CBAUD){ + /* + case B0: cy_writel(&ch_ctrl->comm_baud , 0); break; + */ + case B50: cy_writel(&ch_ctrl->comm_baud , 50); break; + case B75: cy_writel(&ch_ctrl->comm_baud , 75); break; + case B110: cy_writel(&ch_ctrl->comm_baud , 110); break; + case B134: cy_writel(&ch_ctrl->comm_baud , 134); break; + case B150: cy_writel(&ch_ctrl->comm_baud , 150); break; + case B200: cy_writel(&ch_ctrl->comm_baud , 200); break; + case B300: cy_writel(&ch_ctrl->comm_baud , 300); break; + case B600: cy_writel(&ch_ctrl->comm_baud , 600); break; + case B1200: cy_writel(&ch_ctrl->comm_baud , 1200); break; + case B1800: cy_writel(&ch_ctrl->comm_baud , 1800); break; + case B2400: cy_writel(&ch_ctrl->comm_baud , 2400); break; + case B4800: cy_writel(&ch_ctrl->comm_baud , 4800); break; + case B9600: cy_writel(&ch_ctrl->comm_baud , 9600); break; + case B19200: cy_writel(&ch_ctrl->comm_baud , 19200); break; + case B38400: + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){ + cy_writel(&ch_ctrl->comm_baud , 57600); + }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){ + cy_writel(&ch_ctrl->comm_baud , 115200); + }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ + cy_writel(&ch_ctrl->comm_baud , info->baud); + }else{ + cy_writel(&ch_ctrl->comm_baud , 38400); + } + break; + case B57600: cy_writel(&ch_ctrl->comm_baud , 57600); break; +#ifdef B76800 + case B76800: cy_writel(&ch_ctrl->comm_baud , 76800); break; +#endif + case B115200: cy_writel(&ch_ctrl->comm_baud , 115200); break; + case B230400: cy_writel(&ch_ctrl->comm_baud , 230400); break; + case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break; + } + + if ((i = cy_readl(&ch_ctrl->comm_baud)) == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (i) { + info->timeout = (info->xmit_fifo_size*HZ*15/i) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + + /* byte size and parity */ + switch(cflag & CSIZE){ + case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break; + case CS6: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS6); break; + case CS7: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS7); break; + case CS8: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS8); break; + } + if(cflag & CSTOPB){ + cy_writel(&ch_ctrl->comm_data_l, + cy_readl(&ch_ctrl->comm_data_l) | C_DL_2STOP); + }else{ + cy_writel(&ch_ctrl->comm_data_l, + cy_readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); + } + if (cflag & PARENB){ + if (cflag & PARODD){ + cy_writel(&ch_ctrl->comm_parity , C_PR_ODD); + }else{ + cy_writel(&ch_ctrl->comm_parity , C_PR_EVEN); + } + }else{ + cy_writel(&ch_ctrl->comm_parity , C_PR_NONE); + } + + /* CTS flow control flag */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + cy_writel(&ch_ctrl->hw_flow, + cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + cy_writel(&ch_ctrl->hw_flow, + cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS)); + } + + retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L); + if (retval != 0){ + printk("cyc:set_line_char retval at %d was %x\n", + __LINE__, retval); + } + + /* CD sensitivity */ + if (cflag & CLOCAL){ + info->flags &= ~ASYNC_CHECK_CD; + }else{ + info->flags |= ASYNC_CHECK_CD; + } + + if (iflag & IXON){ + cy_writel(&ch_ctrl->sw_flow, + cy_readl(&ch_ctrl->sw_flow) | C_FL_OXX); + } else { + cy_writel(&ch_ctrl->sw_flow, + cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX); + } + + if(i == 0){ /* baud rate is zero, turn off line */ + cy_writel(&ch_ctrl->rs_control, + cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_line_char dropping Z DTR\n"); +#endif + }else{ + cy_writel(&ch_ctrl->rs_control, + cy_readl(&ch_ctrl->rs_control) | C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_line_char raising Z DTR\n"); +#endif + } + + retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:set_line_char retval at %d was %x\n", + __LINE__, retval); + } + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + } + +} /* set_line_char */ - return; -} /* cy_unthrottle */ static int get_serial_info(struct cyclades_port * info, @@ -1850,14 +3476,15 @@ tmp.port = info->card * 0x100 + info->line - cinfo->first_line; tmp.irq = cinfo->irq; tmp.flags = info->flags; - tmp.baud_base = 0; /*!!!*/ tmp.close_delay = info->close_delay; + tmp.baud_base = info->baud; tmp.custom_divisor = 0; /*!!!*/ tmp.hub6 = 0; /*!!!*/ - memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); + copy_to_user(retinfo,&tmp,sizeof(*retinfo)); return 0; } /* get_serial_info */ + static int set_serial_info(struct cyclades_port * info, struct serial_struct * new_info) @@ -1866,18 +3493,20 @@ struct cyclades_port old_info; if (!new_info) - return -EFAULT; - memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); + return -EFAULT; + copy_from_user(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { - if ((new_serial.close_delay != info->close_delay) || + if ((new_serial.close_delay != info->close_delay) || + (new_serial.baud_base != info->baud) || ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - goto check_and_exit; + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->baud = new_serial.baud_base; + goto check_and_exit; } @@ -1886,20 +3515,23 @@ * At this point, we start making changes..... */ + info->baud = new_serial.baud_base; info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->close_delay = new_serial.close_delay; - + (new_serial.flags & ASYNC_FLAGS)); + info->close_delay = new_serial.close_delay * HZ/100; + info->closing_wait = new_serial.closing_wait * HZ/100; + check_and_exit: if (info->flags & ASYNC_INITIALIZED){ - config_setup(info); - return 0; + set_line_char(info); + return 0; }else{ return startup(info); } } /* set_serial_info */ + static int get_modem_info(struct cyclades_port * info, unsigned int *value) { @@ -1907,32 +3539,73 @@ unsigned char *base_addr; unsigned long flags; unsigned char status; + unsigned long lstatus; unsigned int result; + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<rtsdtr_inv) { + result = ((status & CyRTS) ? TIOCM_DTR : 0) + | ((status & CyDTR) ? TIOCM_RTS : 0); + } else { + result = ((status & CyRTS) ? TIOCM_RTS : 0) + | ((status & CyDTR) ? TIOCM_DTR : 0); + } + result |= ((status & CyDCD) ? TIOCM_CAR : 0) + | ((status & CyRI) ? TIOCM_RNG : 0) + | ((status & CyDSR) ? TIOCM_DSR : 0) + | ((status & CyCTS) ? TIOCM_CTS : 0); + } else { + base_addr = (unsigned char*) (cy_card[card].base_addr); + + if (cy_card[card].num_chips != -1){ + return -EINVAL; + } + + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (ISZLOADED(cy_card[card])) { + zfw_ctrl = (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + lstatus = cy_readl(&ch_ctrl[channel].rs_status); + result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) + | ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) + | ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) + | ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) + | ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) + | ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); + }else{ + result = 0; + return -ENODEV; + } - result = ((status & CyRTS) ? TIOCM_RTS : 0) - | ((status & CyDTR) ? TIOCM_DTR : 0) - | ((status & CyDCD) ? TIOCM_CAR : 0) - | ((status & CyRI) ? TIOCM_RNG : 0) - | ((status & CyDSR) ? TIOCM_DSR : 0) - | ((status & CyCTS) ? TIOCM_CTS : 0); - put_fs_long(result,(unsigned long *) value); + } + cy_put_user(result,(unsigned long *) value); return 0; } /* get_modem_info */ + static int set_modem_info(struct cyclades_port * info, unsigned int cmd, unsigned int *value) @@ -1940,221 +3613,398 @@ int card,chip,channel,index; unsigned char *base_addr; unsigned long flags; - unsigned int arg = get_fs_long((unsigned long *) value); + unsigned int arg = cy_get_user((unsigned long *) value); + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<zfwctrl_addr)); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); + } + if (arg & TIOCM_DTR){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_modem_info raising Z DTR\n"); +#endif + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); + } + if (arg & TIOCM_DTR){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_modem_info clearing Z DTR\n"); +#endif + } + break; + case TIOCMSET: + if (arg & TIOCM_RTS){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); + }else{ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); + } + if (arg & TIOCM_DTR){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_modem_info raising Z DTR\n"); +#endif + }else{ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); +#ifdef CY_DEBUG_DTR + printk("cyc:set_modem_info clearing Z DTR\n"); +#endif + } + break; + default: return -EINVAL; - } + } + }else{ + return -ENODEV; + } + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM,0L); + if (retval != 0){ + printk("cyc:set_modem_info retval at %d was %x\n", + __LINE__, retval); + } + } return 0; } /* set_modem_info */ + static void send_break( struct cyclades_port * info, int duration) -{ /* Let the transmit ISR take care of this (since it - requires stuffing characters into the output stream). - */ - info->x_break = duration; - if (!info->xmit_cnt ) { - start_xmit(info); +{ + + if (!IS_CYC_Z(cy_card[info->card])) { + /* Let the transmit ISR take care of this (since it + requires stuffing characters into the output stream). + */ + info->x_break = duration; + if (!info->xmit_cnt ) { + start_xmit(info); + } + } else { + /* For the moment we ignore the duration parameter!!! + A better implementation will use C_CM_SET_BREAK + and C_CM_CLR_BREAK with the appropriate delay. + */ +#if 1 +// this appears to wedge the output data stream +int retval; + retval = cyz_issue_cmd(&cy_card[info->card], + (info->line) - (cy_card[info->card].first_line), + C_CM_SENDBRK, 0L); + if (retval != 0){ + printk("cyc:send_break retval at %d was %x\n", + __LINE__, retval); + } +#endif } } /* send_break */ + static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) { - memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor)); - info->mon.int_count = 0; - info->mon.char_count = 0; - info->mon.char_max = 0; - info->mon.char_last = 0; - return 0; -} + copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); + info->mon.int_count = 0; + info->mon.char_count = 0; + info->mon.char_max = 0; + info->mon.char_last = 0; + return 0; +}/* get_mon_info */ + static int set_threshold(struct cyclades_port * info, unsigned long value) { - unsigned char *base_addr; - int card,channel,chip,index; + unsigned char *base_addr; + int card,channel,chip,index; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<cor3 &= ~CyREC_FIFO; + info->cor3 |= value & CyREC_FIFO; + cy_writeb((u_long)base_addr+(CyCOR3<cor3); + cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); + } else { + // Nothing to do! + } + return 0; +}/* set_threshold */ - info->cor3 &= ~CyREC_FIFO; - info->cor3 |= value & CyREC_FIFO; - base_addr[CyCOR3<cor3; - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); - return 0; -} static int get_threshold(struct cyclades_port * info, unsigned long *value) { - unsigned char *base_addr; - int card,channel,chip,index; - unsigned long tmp; + unsigned char *base_addr; + int card,channel,chip,index; + unsigned long tmp; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<default_threshold = value & 0x0f; - return 0; -} + info->default_threshold = value & 0x0f; + return 0; +}/* set_default_threshold */ + static int get_default_threshold(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_threshold,value); - return 0; -} + cy_put_user(info->default_threshold,value); + return 0; +}/* get_default_threshold */ + static int set_timeout(struct cyclades_port * info, unsigned long value) { - unsigned char *base_addr; - int card,channel,chip,index; + unsigned char *base_addr; + int card,channel,chip,index; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<default_timeout = value & 0xff; - return 0; -} + info->default_timeout = value & 0xff; + return 0; +}/* set_default_timeout */ + static int get_default_timeout(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_timeout,value); - return 0; -} + cy_put_user(info->default_timeout,value); + return 0; +}/* get_default_timeout */ +/* + * This routine allows the tty driver to implement device- + * specific ioctl's. If the ioctl number passed in cmd is + * not recognized by the driver, it should return ENOIOCTLCMD. + */ static int cy_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -2163,8 +4013,9 @@ struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; int ret_val = 0; -#ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */ +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", + info->line, cmd, arg); /* */ #endif switch (cmd) { @@ -2176,7 +4027,7 @@ break; } ret_val = get_mon_info(info, (struct cyclades_monitor *)arg); - break; + break; case CYGETTHRESH: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2184,11 +4035,11 @@ ret_val = error; break; } - ret_val = get_threshold(info, (unsigned long *)arg); - break; + ret_val = get_threshold(info, (unsigned long *)arg); + break; case CYSETTHRESH: ret_val = set_threshold(info, (unsigned long)arg); - break; + break; case CYGETDEFTHRESH: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2196,11 +4047,11 @@ ret_val = error; break; } - ret_val = get_default_threshold(info, (unsigned long *)arg); - break; + ret_val = get_default_threshold(info, (unsigned long *)arg); + break; case CYSETDEFTHRESH: ret_val = set_default_threshold(info, (unsigned long)arg); - break; + break; case CYGETTIMEOUT: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2208,11 +4059,11 @@ ret_val = error; break; } - ret_val = get_timeout(info, (unsigned long *)arg); - break; + ret_val = get_timeout(info, (unsigned long *)arg); + break; case CYSETTIMEOUT: ret_val = set_timeout(info, (unsigned long)arg); - break; + break; case CYGETDEFTIMEOUT: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2220,23 +4071,65 @@ ret_val = error; break; } - ret_val = get_default_timeout(info, (unsigned long *)arg); - break; + ret_val = get_default_timeout(info, (unsigned long *)arg); + break; case CYSETDEFTIMEOUT: ret_val = set_default_timeout(info, (unsigned long)arg); + break; + case CYSETRFLOW: + info->rflow = (int)arg; + ret_val = 0; + break; + case CYGETRFLOW: + ret_val = info->rflow; + break; + case CYSETRTSDTR_INV: + info->rtsdtr_inv = (int)arg; + ret_val = 0; + break; + case CYGETRTSDTR_INV: + ret_val = info->rtsdtr_inv; + break; + case CYGETCARDINFO: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(struct cyclades_card)); + if (error){ + ret_val = error; + break; + } + copy_to_user((void *)arg, (void *)&cy_card[info->card], + sizeof (struct cyclades_card)); + ret_val = 0; + break; + case CYGETCD1400VER: + ret_val = info->chip_rev; + break; + case CYZSETPOLLCYCLE: + cyz_polling_cycle = (arg * HZ) / 1000; + ret_val = 0; + break; + case CYZGETPOLLCYCLE: + ret_val = (cyz_polling_cycle * 1000) / HZ; + break; + case CYSETWAIT: + info->closing_wait = (unsigned short)arg * HZ/100; + ret_val = 0; + break; + case CYGETWAIT: + ret_val = info->closing_wait / (HZ/100); break; case TCSBRK: /* SVID version: non-zero arg --> no break */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; tty_wait_until_sent(tty,0); if (!arg) send_break(info, HZ/4); /* 1/4 second */ break; case TCSBRKP: /* support for POSIX tcsendbreak() */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; tty_wait_until_sent(tty,0); send_break(info, arg ? arg*(HZ/10) : HZ/4); break; @@ -2254,18 +4147,18 @@ ret_val = error; break; } - put_fs_long(C_CLOCAL(tty) ? 1 : 0, + cy_put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); break; case TIOCSSOFTCAR: - error = verify_area(VERIFY_READ, (void *) arg - ,sizeof(unsigned long *)); - if (error) { - ret_val = error; - break; - } + error = verify_area(VERIFY_READ, (void *) arg + ,sizeof(unsigned long *)); + if (error) { + ret_val = error; + break; + } - arg = get_fs_long((unsigned long *) arg); + arg = cy_get_user((unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); @@ -2300,31 +4193,35 @@ (struct serial_struct *) arg); break; default: - ret_val = -ENOIOCTLCMD; + ret_val = -ENOIOCTLCMD; } -#ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl done\n"); +#ifdef CY_DEBUG_OTHER + printk(" cyc:cy_ioctl done\n"); #endif return ret_val; } /* cy_ioctl */ - - +/* + * This routine allows the tty driver to be notified when + * device's termios settings have changed. Note that a + * well-designed tty driver should be prepared to accept the case + * where old == NULL, and try to do something rational. + */ static void cy_set_termios(struct tty_struct *tty, struct termios * old_termios) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; -#ifdef SERIAL_DEBUG_OTHER - printk("cy_set_termios ttyC%d\n", info->line); +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_set_termios ttyC%d\n", info->line); #endif if (tty->termios->c_cflag == old_termios->c_cflag) return; - config_setup(info); + set_line_char(info); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -2341,353 +4238,281 @@ } /* cy_set_termios */ +/* + * void (*set_ldisc)(struct tty_struct *tty); + * + * This routine allows the tty driver to be notified when the + * device's termios settings have changed. + * + */ + + +/* This routine is called by the upper-layer tty layer to signal + that incoming characters should be throttled because the input + buffers are close to full. + */ static void -cy_close(struct tty_struct * tty, struct file * filp) +cy_throttle(struct tty_struct * tty) { - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close ttyC%d\n", info->line); -#endif +#ifdef CY_DEBUG_THROTTLE + char buf[64]; - if (!info - || serial_paranoia_check(info, tty->device, "cy_close")){ - return; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_close ttyC%d, count = %d\n", info->line, info->count); + printk("cyc:throttle %s: %d....ttyC%d\n", + _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); #endif - save_flags(flags); cli(); - - /* If the TTY is being hung up, nothing to do */ - if (tty_hung_up_p(filp)) { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; - } - - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("cy_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count - 1); -#endif - if (--info->count < 0) { -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->count = 0; + if (serial_paranoia_check(info, tty->device, "cy_throttle")){ + return; } - if (info->count) - { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; + + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ } - info->flags |= ASYNC_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - if (info->flags & ASYNC_CALLOUT_ACTIVE) - info->callout_termios = *tty->termios; - if (info->flags & ASYNC_INITIALIZED) - tty_wait_until_sent(tty, 30*HZ); /* 30 seconds timeout */ - shutdown(info); - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - info->event = 0; - info->tty = 0; - if (info->blocked_open) { - if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<open_wait); + restore_flags(flags); + } else { + // Nothing to do! } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| - ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close done\n"); -#endif - - MOD_DEC_USE_COUNT; - restore_flags(flags); return; -} /* cy_close */ +} /* cy_throttle */ + /* - * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + * This routine notifies the tty driver that it should signal + * that characters can now be sent to the tty without fear of + * overrunning the input buffers of the line disciplines. */ -void -cy_hangup(struct tty_struct *tty) +static void +cy_unthrottle(struct tty_struct * tty) { - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_hangup ttyC%d\n", info->line); /* */ -#endif + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; - if (serial_paranoia_check(info, tty->device, "cy_hangup")) - return; - - shutdown(info); - info->event = 0; - info->count = 0; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): setting count to 0\n", __LINE__, current->pid); +#ifdef CY_DEBUG_THROTTLE + char buf[64]; + + printk("cyc:unthrottle %s: %d....ttyC%d\n", + _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); #endif - info->tty = 0; - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); - wake_up_interruptible(&info->open_wait); -} /* cy_hangup */ + if (serial_paranoia_check(info, tty->device, "cy_unthrottle")){ + return; + } + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } -/* - * ------------------------------------------------------------ - * cy_open() and friends - * ------------------------------------------------------------ - */ + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<driver_data; + unsigned char *base_addr; + int chip,channel,index; unsigned long flags; - int chip, channel,index; - int retval; - char *base_addr; - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); - if (info->flags & ASYNC_HUP_NOTIFY){ - return -EAGAIN; - }else{ - return -ERESTARTSYS; - } - } +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_stop ttyC%d\n", info->line); /* */ +#endif - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { - if (info->flags & ASYNC_NORMAL_ACTIVE){ - return -EBUSY; - } - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_SESSION_LOCKOUT) && - (info->session != current->session)){ - return -EBUSY; - } - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_PGRP_LOCKOUT) && - (info->pgrp != current->pgrp)){ - return -EBUSY; - } - info->flags |= ASYNC_CALLOUT_ACTIVE; - return 0; + if (serial_paranoia_check(info, tty->device, "cy_stop")) + return; + + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + if (!IS_CYC_Z(*cinfo)) { + index = cinfo->bus_index; + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<f_flags & O_NONBLOCK) { - if (info->flags & ASYNC_CALLOUT_ACTIVE){ - return -EBUSY; - } - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } + return; +} /* cy_stop */ - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * cy_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - info->count--; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count); + +static void +cy_start(struct tty_struct *tty) +{ + struct cyclades_card *cinfo; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned char *base_addr; + int chip,channel,index; + unsigned long flags; + +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_start ttyC%d\n", info->line); /* */ #endif - info->blocked_open++; + if (serial_paranoia_check(info, tty->device, "cy_start")) + return; + cinfo = &cy_card[info->card]; channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; index = cinfo->bus_index; - base_addr = (char *) (cinfo->base_addr + (cy_chip_offset[chip]<flags & ASYNC_CALLOUT_ACTIVE)){ - base_addr[CyCAR<state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp) - || !(info->flags & ASYNC_INITIALIZED) ){ - if (info->flags & ASYNC_HUP_NOTIFY) { - retval = -EAGAIN; - }else{ - retval = -ERESTARTSYS; - } - break; - } - save_flags(flags); cli(); - base_addr[CyCAR<flags & ASYNC_CALLOUT_ACTIVE) - && !(info->flags & ASYNC_CLOSING) - && (C_CLOCAL(tty) - || (base_addr[CyMSVR1<signal & ~current->blocked) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)){ - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); -#endif + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} /* block_til_ready */ -/* - * This routine is called whenever a serial port is opened. It - * performs the serial-specific initialization for the tty structure. - */ -int -cy_open(struct tty_struct *tty, struct file * filp) -{ - struct cyclades_port *info; - int retval, line; + return; +} /* cy_start */ - line = MINOR(tty->device) - tty->driver.minor_start; - if ((line < 0) || (NR_PORTS <= line)){ - return -ENODEV; - } - info = &cy_port[line]; - if (info->line < 0){ - return -ENODEV; - } -#ifdef SERIAL_DEBUG_OTHER - printk("cy_open ttyC%d\n", info->line); /* */ -#endif - if (serial_paranoia_check(info, tty->device, "cy_open")){ - return -ENODEV; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open ttyC%d, count = %d\n", info->line, info->count);/**/ -#endif - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); + +static void +cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + int card, channel; + unsigned long flags; + +#ifdef CY_DEBUG_IO + printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */ #endif - tty->driver_data = info; - info->tty = tty; - if (!tmp_buf) { - tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); - if (!tmp_buf){ - return -ENOMEM; - } - } + if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + + card = info->card; + channel = (info->line) - (cy_card[card].first_line); - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { - if (tty->driver.subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->normal_termios; - else - *tty->termios = info->callout_termios; - } - /* - * Start up serial port - */ - retval = startup(info); - if (retval){ - return retval; + if (IS_CYC_Z(cy_card[card])) { /* If it is a Z card, flush the on-board + buffers as well */ + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; + + firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS); + zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + + cy_readl(&firm_id->zfwctrl_addr)); + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + + while (cy_readl(&buf_ctrl->tx_get) != cy_readl(&buf_ctrl->tx_put)) + cy_writel(&buf_ctrl->tx_put, cy_readl(&buf_ctrl->tx_get)); } + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} /* cy_flush_buffer */ - MOD_INC_USE_COUNT; - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open returning after block_til_ready with %d\n", - retval); +/* + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void +cy_hangup(struct tty_struct *tty) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + +#ifdef CY_DEBUG_OTHER + printk("cyc:cy_hangup ttyC%d\n", info->line); /* */ #endif - return retval; - } - info->session = current->session; - info->pgrp = current->pgrp; + if (serial_paranoia_check(info, tty->device, "cy_hangup")) + return; -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open done\n");/**/ + cy_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; +#ifdef CY_DEBUG_COUNT + printk("cyc:cy_hangup (%d): setting count to 0\n", current->pid); #endif - return 0; -} /* cy_open */ - + info->tty = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&info->open_wait); +} /* cy_hangup */ /* @@ -2698,33 +4523,25 @@ * --------------------------------------------------------------------- */ -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static void -show_version(void) -{ - printk("Cyclom driver %s\n",rcsid); -} /* show_version */ - -/* initialize chips on card -- return number of valid +/* initialize chips on Cyclom-Y card -- return number of valid chips (which is number of ports/4) */ -int -cy_init_card(unsigned char *true_base_addr,int index) +__initfunc(static unsigned short +cyy_init_card(volatile ucchar *true_base_addr,int index)) { unsigned int chip_number; - unsigned char* base_addr; + volatile ucchar* base_addr; - true_base_addr[Cy_HwReset<= CD1400_REV_J){ + /* It is a CD1400 rev. J or later */ + /* Impossible to reach 5ms with this chip. + Changed to 2ms instead (f = 500 Hz). */ + cy_writeb((u_long)base_addr+(CyPPR< NR_PORTS) { + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); + printk("but no more channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(nboard); + } + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); + printk("but no more cards can be used .\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(nboard); + } + + /* allocate IRQ */ + if(request_irq(cy_isa_irq, cyy_interrupt, + SA_INTERRUPT, "cyclomY", NULL)) + { + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); + printk("but could not allocate IRQ#%d.\n", + cy_isa_irq); + return(nboard); + } + + /* set cy_card */ + cy_card[j].base_addr = (u_long) cy_isa_address; + cy_card[j].ctl_addr = 0; + cy_card[j].irq = (int) cy_isa_irq; + cy_card[j].bus_index = 0; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_isa_nchan/4; + IRQ_cards[cy_isa_irq] = &cy_card[j]; + nboard++; + + /* print message */ + printk("Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1, (unsigned long) cy_isa_address, + (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), + cy_isa_irq); + printk("%d channels starting from port %d.\n", + cy_isa_nchan, cy_next_channel); + cy_next_channel += cy_isa_nchan; + } + return(nboard); + +} /* cy_detect_isa */ + +/* + * --------------------------------------------------------------------- + * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI. + * sets global variables and return the number of PCI boards found. + * --------------------------------------------------------------------- + */ +__initfunc(static int +cy_detect_pci(void)) +{ +#ifdef CONFIG_PCI + unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; + unsigned long pci_intr_ctrl; + unsigned char cy_pci_irq; + uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; + unsigned short i,j,cy_pci_nchan, plx_ver; + unsigned short device_id,dev_index = 0,board_index = 0; + uclong mailbox; + uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; + + if(pcibios_present() == 0) { /* PCI bus not present */ + return(0); + } + for (i = 0; i < NR_CARDS; i++) { + /* look for a Cyclades card by vendor and device id */ + while((device_id = cy_pci_dev_id[dev_index]) != 0) { + if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, + device_id,board_index, + &cyy_bus, &cyy_dev_fn) != 0) + { + dev_index++; /* try next device id */ + board_index = 0; + } else { + board_index++; + break; /* found a board */ + } + } + + if (device_id == 0) + break; + + /* read PCI configuration area */ + pcibios_read_config_byte(cyy_bus, cyy_dev_fn, + PCI_INTERRUPT_LINE, &cy_pci_irq); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_0, + (unsigned int *) &cy_pci_addr0); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_1, + (unsigned int *) &cy_pci_addr1); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_2, + (unsigned int *) &cy_pci_addr2); + pcibios_read_config_byte(cyy_bus, cyy_dev_fn, + PCI_REVISION_ID, &cyy_rev_id); + + if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) + || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ +#ifdef CY_PCI_DEBUG + printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr1); +#endif + cy_pci_addr1 &= PCI_BASE_ADDRESS_IO_MASK; + cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; + +#if defined(__alpha__) + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ + printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr1); + printk("Cyclom-Y/PCI not supported for low addresses in " + "Alpha systems.\n"); + i--; + continue; + } +#else + if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ + cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin); +#endif + +#ifdef CY_PCI_DEBUG + printk("Cyclom-Y/PCI: relocate winaddr=0x%lx ioaddr=0x%lx\n", + (u_long)cy_pci_addr2, (u_long)cy_pci_addr1); +#endif + cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP * + cyy_init_card((volatile ucchar *)cy_pci_addr2, 1)); + if(cy_pci_nchan == 0) { + printk("Cyclom-Y PCI host card with "); + printk("no Serial-Modules at 0x%lx.\n", + (ulong) cy_pci_addr2); + i--; + continue; + } + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ */ + if(request_irq(cy_pci_irq, cyy_interrupt, + SA_INTERRUPT, "cyclomY", NULL)) + { + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); + printk("but could not allocate IRQ%d.\n", + cy_pci_irq); + return(i); + } + + /* set cy_card */ + cy_card[j].base_addr = (ulong)cy_pci_addr2; + cy_card[j].ctl_addr = 0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_pci_nchan/4; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* enable interrupts in the PCI interface */ + plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f; + switch (plx_ver) { + case PLX_9050: + + outw(inw(cy_pci_addr1+0x4c)|0x0040,cy_pci_addr1+0x4c); + pci_intr_ctrl = (unsigned long) + (inw(cy_pci_addr1+0x4c) + | inw(cy_pci_addr1+0x4e)<<16); + break; + + case PLX_9060: + case PLX_9080: + default: /* Old boards, use PLX_9060 */ + + outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68); + pci_intr_ctrl = (unsigned long) + (inw(cy_pci_addr1+0x68) + | inw(cy_pci_addr1+0x6a)<<16); + break; + } + + /* print message */ + printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1, + (ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ywin - 1), + (int)cy_pci_irq); + printk("%d channels starting from port %d.\n", + cy_pci_nchan, cy_next_channel); + + cy_next_channel += cy_pci_nchan; + }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ + /* print message */ + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); + printk("Cyclades-Z/PCI not supported for low addresses\n"); + break; + }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); +#endif + cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK; +#if !defined(__alpha__) + cy_pci_addr0 = (unsigned int) ioremap( + cy_pci_addr0 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zctl)) + + (cy_pci_addr0 & (PAGE_SIZE-1)); +#endif + mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) + cy_pci_addr0)->mail_box_0); + cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; + if (mailbox == ZE_V1) { +#if !defined(__alpha__) + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Ze_win)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); +#endif + if (ZeIndex == NR_CARDS) { + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } else { + Ze_addr0[ZeIndex] = cy_pci_addr0; + Ze_addr2[ZeIndex] = cy_pci_addr2; + ZeIndex++; + } + i--; + continue; + } else { +#if !defined(__alpha__) + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zwin)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); +#endif + } + +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); + if (mailbox == ZO_V1) { + cy_writel(&((struct RUNTIME_9060 *) + (cy_pci_addr0))->loc_addr_base, WIN_CREG); + PAUSE + printk("Cyclades-8Zo/PCI: FPGA id %lx, ver %lx\n", + (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *) + (cy_pci_addr2))->fpga_id)), + (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *) + (cy_pci_addr2))->fpga_version))); + cy_writel(&((struct RUNTIME_9060 *) + (cy_pci_addr0))->loc_addr_base, WIN_RAM); + } else { + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); + } +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) + cy_writel((ulong)(cy_pci_addr2+ID_ADDRESS), 0L); + + /* This must be a Cyclades-8Zo/PCI. The extendable + version will have a different device_id and will + be allocated its maximum number of ports. */ + cy_pci_nchan = 8; + + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-8Zo/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclades-8Zo/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + cy_pci_irq); + printk("for Cyclades-8Zo/PCI at 0x%lx.\n", + (ulong)cy_pci_addr2); + return(i); + } + } + + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = -1; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + } + + for (; ZeIndex != 0 && i < NR_CARDS; i++) { + cy_pci_addr0 = Ze_addr0[0]; + cy_pci_addr2 = Ze_addr2[0]; + for (j = 0 ; j < ZeIndex-1 ; j++) { + Ze_addr0[j] = Ze_addr0[j+1]; + Ze_addr2[j] = Ze_addr2[j+1]; + } + ZeIndex--; + mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) + cy_pci_addr0)->mail_box_0); +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + /* This must be the new Cyclades-Ze/PCI. */ + cy_pci_nchan = ZE_V1_NPORTS; + + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + cy_pci_irq); + printk("for Cyclades-Ze/PCI at 0x%lx.\n", + (ulong) cy_pci_addr2); + return(i); + } + } + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = -1; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + if (ZeIndex != 0) { + printk("Cyclades-Ze/PCI found at 0x%x ", + (unsigned int) Ze_addr2[0]); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } + return(i); +#else + return(0); +#endif /* ifdef CONFIG_PCI */ +} /* cy_detect_pci */ + + +/* + * This routine prints out the appropriate serial driver version number + * and identifies which options were configured into this driver. + */ +static inline void +show_version(void) +{ + char *rcsvers, *rcsdate, *tmp; + rcsvers = strchr(rcsid, ' '); rcsvers++; + tmp = strchr(rcsvers, ' '); *tmp++ = '\0'; + rcsdate = strchr(tmp, ' '); rcsdate++; + tmp = strrchr(rcsdate, ' '); *tmp = '\0'; + printk("Cyclades driver %s %s\n", + rcsvers, rcsdate); + printk(" built %s %s\n", + __DATE__, __TIME__); +} /* show_version */ + +#if defined(CONFIG_PROC_FS) && !defined(MODULE) +int cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, + int dummy) +{ + struct cyclades_port *info; + int i; + int len=0; + off_t begin=0; + off_t pos=0; + int size; + __u32 cur_jifs = jiffies; + + size = sprintf(buf+len, "Dev TimeOpen BytesOut IdleOut BytesIn IdleIn Overruns Ldisc\n"); + + len += size; + pos += size; + + /* Output one line for each known port */ + for (i = 0; i < NR_PORTS && cy_port[i].line >= 0; i++) { + info = &cy_port[i]; + + if (info->count) + size = sprintf(buf+len, + "%3d %8lu %10lu %8lu %10lu %8lu %9lu %6d\n", + info->line, + JIFFIES_DIFF(info->idle_stats.in_use, cur_jifs) / HZ, + info->idle_stats.xmit_bytes, + JIFFIES_DIFF(info->idle_stats.xmit_idle, cur_jifs) / HZ, + info->idle_stats.recv_bytes, + JIFFIES_DIFF(info->idle_stats.recv_idle, cur_jifs) / HZ, + info->idle_stats.overruns, + info->tty->ldisc.num); + else + size = sprintf(buf+len, + "%3d %8lu %10lu %8lu %10lu %8lu %9lu %6ld\n", + info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + break; + } + + *start = buf + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; /* Ending slop */ + return len; +} +#endif + /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a @@ -2789,15 +5196,21 @@ device driver because the Cyclom is more properly a multiplexer, not just an aggregation of serial ports on one card. - If there are more cards with more ports than have been statically - allocated above, a warning is printed and the extra ports are ignored. + If there are more cards with more ports than have been + statically allocated above, a warning is printed and the + extra ports are ignored. */ -int -cy_init(void) + +__initfunc(int +cy_init(void)) { - struct cyclades_port *info; + struct cyclades_port *info; struct cyclades_card *cinfo; - int board,port,i; + int number_z_boards = 0; + int board,port,i,index; + unsigned long mailbox; + unsigned short chip_number; + int nports; show_version(); @@ -2813,7 +5226,7 @@ cy_serial_driver.subtype = SERIAL_TYPE_NORMAL; cy_serial_driver.init_termios = tty_std_termios; cy_serial_driver.init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; + B9600 | CS8 | CREAD | HUPCL | CLOCAL; cy_serial_driver.flags = TTY_DRIVER_REAL_RAW; cy_serial_driver.refcount = &serial_refcount; cy_serial_driver.table = serial_table; @@ -2845,31 +5258,31 @@ cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT; if (tty_register_driver(&cy_serial_driver)) - panic("Couldn't register Cyclom serial driver\n"); + panic("Couldn't register Cyclades serial driver\n"); if (tty_register_driver(&cy_callout_driver)) - panic("Couldn't register Cyclom callout driver\n"); + panic("Couldn't register Cyclades callout driver\n"); init_bh(CYCLADES_BH, do_cyclades_bh); for (i = 0; i < 16; i++) { - IRQ_cards[i] = 0; + IRQ_cards[i] = 0; } for (i = 0; i < NR_CARDS; i++) { - /* base_addr=0 indicates board not found */ - cy_card[i].base_addr = 0; + /* base_addr=0 indicates board not found */ + cy_card[i].base_addr = 0; } /* the code below is responsible to find the boards. Each different type of board has its own detection routine. If a board is found, the next cy_card structure available is set by the detection - routine. These functions are responsible for checking the availability - of cy_card and cy_port data structures and updating the - cy_next_channel. */ + routine. These functions are responsible for checking the + availability of cy_card and cy_port data structures and updating + the cy_next_channel. */ /* look for isa boards */ cy_isa_nboard = cy_detect_isa(); - + /* look for pci boards */ cy_pci_nboard = cy_detect_pci(); @@ -2877,288 +5290,229 @@ /* invalidate remaining cy_card structures */ for (i = 0 ; i < NR_CARDS ; i++) { - if (cy_card[i].base_addr == 0) { - cy_card[i].first_line = -1; - } + if (cy_card[i].base_addr == 0) { + cy_card[i].first_line = -1; + cy_card[i].ctl_addr = 0; + cy_card[i].irq = 0; + cy_card[i].bus_index = 0; + cy_card[i].first_line = 0; + cy_card[i].num_chips = 0; + } } /* invalidate remaining cy_port structures */ for (i = cy_next_channel ; i < NR_PORTS ; i++) { - cy_port[i].line = -1; - cy_port[i].magic = -1; + cy_port[i].line = -1; + cy_port[i].magic = -1; } /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { - cinfo = &cy_card[board]; - for (port = cinfo->first_line ; - port < cinfo->first_line + 4*cinfo->num_chips ; - port++) - { - info = &cy_port[port]; - info->magic = CYCLADES_MAGIC; - info->type = PORT_CIRRUS; - info->card = board; - info->line = port; - info->flags = STD_COM_FLAGS; - info->tty = 0; - info->xmit_fifo_size = 12; - info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; - info->cor2 = CyETC; - info->cor3 = 0x08; /* _very_ small receive threshold */ - info->cor4 = 0; - info->cor5 = 0; - info->tbpr = baud_bpr[13]; /* Tx BPR */ - info->tco = baud_co[13]; /* Tx CO */ - info->rbpr = baud_bpr[13]; /* Rx BPR */ - info->rco = baud_co[13]; /* Rx CO */ - info->close_delay = 0; - info->x_char = 0; - info->event = 0; - info->count = 0; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->blocked_open = 0; - info->default_threshold = 0; - info->default_timeout = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - info->callout_termios =cy_callout_driver.init_termios; - info->normal_termios = cy_serial_driver.init_termios; - info->open_wait = 0; - info->close_wait = 0; - /* info->session */ - /* info->pgrp */ - info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK - | CyPARITY| CyFRAME| CyOVERRUN; - /* info->timeout */ - } + cinfo = &cy_card[board]; + if (cinfo->num_chips == -1){ /* Cyclades-Z */ + number_z_boards++; + mailbox = cy_readl(&((struct RUNTIME_9060 *) + cy_card[board].ctl_addr)->mail_box_0); + nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8; + for (port = cinfo->first_line ; + port < cinfo->first_line + nports; + port++) + { + info = &cy_port[port]; + info->magic = CYCLADES_MAGIC; + info->type = PORT_STARTECH; + info->card = board; + info->line = port; + info->chip_rev = 0; + info->flags = STD_COM_FLAGS; + info->tty = 0; + if (mailbox == ZO_V1) + info->xmit_fifo_size = CYZ_FIFO_SIZE; + else + info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; + info->cor1 = 0; + info->cor2 = 0; + info->cor3 = 0; + info->cor4 = 0; + info->cor5 = 0; + info->tbpr = 0; + info->tco = 0; + info->rbpr = 0; + info->rco = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = CLOSING_WAIT_DELAY; + info->x_char = 0; + info->event = 0; + info->count = 0; +#ifdef CY_DEBUG_COUNT +// printk("cyc:cy_init(1) setting Z count to 0\n"); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = + cy_callout_driver.init_termios; + info->normal_termios = + cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + info->shutdown_wait = 0; + /* info->session */ + /* info->pgrp */ + info->read_status_mask = 0; + /* info->timeout */ + /* Bentson's vars */ + info->jiffies[0] = 0; + info->jiffies[1] = 0; + info->jiffies[2] = 0; + info->rflush_count = 0; + } + continue; + }else{ /* Cyclom-Y of some kind*/ + index = cinfo->bus_index; + for (port = cinfo->first_line ; + port < cinfo->first_line + 4*cinfo->num_chips ; + port++) + { + info = &cy_port[port]; + info->magic = CYCLADES_MAGIC; + info->type = PORT_CIRRUS; + info->card = board; + info->line = port; + info->flags = STD_COM_FLAGS; + info->tty = 0; + info->xmit_fifo_size = CyMAX_CHAR_FIFO; + info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; + info->cor2 = CyETC; + info->cor3 = 0x08; /* _very_ small rcv threshold */ + info->cor4 = 0; + info->cor5 = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = CLOSING_WAIT_DELAY; + chip_number = (port - cinfo->first_line) / 4; + if ((info->chip_rev = cy_readb(cinfo->base_addr + + (cy_chip_offset[chip_number]<= CD1400_REV_J) { + /* It is a CD1400 rev. J or later */ + info->tbpr = baud_bpr_60[13]; /* Tx BPR */ + info->tco = baud_co_60[13]; /* Tx CO */ + info->rbpr = baud_bpr_60[13]; /* Rx BPR */ + info->rco = baud_co_60[13]; /* Rx CO */ + info->rflow = 0; + info->rtsdtr_inv = 1; + } else { + info->tbpr = baud_bpr_25[13]; /* Tx BPR */ + info->tco = baud_co_25[13]; /* Tx CO */ + info->rbpr = baud_bpr_25[13]; /* Rx BPR */ + info->rco = baud_co_25[13]; /* Rx CO */ + info->rflow = 0; + info->rtsdtr_inv = 0; + } + info->x_char = 0; + info->event = 0; + info->count = 0; +#ifdef CY_DEBUG_COUNT +// printk("cyc:cy_init(2) setting Y count to 0\n"); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = + cy_callout_driver.init_termios; + info->normal_termios = + cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + info->shutdown_wait = 0; + /* info->session */ + /* info->pgrp */ + info->read_status_mask = + CyTIMEOUT| CySPECHAR| CyBREAK + | CyPARITY| CyFRAME| CyOVERRUN; + /* info->timeout */ + } + } + } + + if ( number_z_boards && !cyz_timeron){ + cyz_timeron++; + cyz_timerlist.expires = jiffies + 1; + add_timer(&cyz_timerlist); +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z polling initialized\n"); +#endif } + +#if defined(CONFIG_PROC_FS) && !defined(MODULE) + proc_register_dynamic(&proc_root, &cyclades_proc_entry); +#endif + return 0; } /* cy_init */ #ifdef MODULE +/* See linux/drivers/char/riscom.c for ideas on how to + pass additional base addresses to the driver!!! */ int init_module(void) { return(cy_init()); -} +} /* init_module */ void cleanup_module(void) { int i; + unsigned long flags; + + if (cyz_timeron){ + cyz_timeron = 0; + del_timer(&cyz_timerlist); + } + save_flags(flags); cli(); + free_page((unsigned long)tmp_buf); if (tty_unregister_driver(&cy_callout_driver)) - printk("Couldn't unregister Cyclom callout driver\n"); + printk("Couldn't unregister Cyclades callout driver\n"); if (tty_unregister_driver(&cy_serial_driver)) - printk("Couldn't unregister Cyclom serial driver\n"); + printk("Couldn't unregister Cyclades serial driver\n"); + + restore_flags(flags); for (i = 0; i < NR_CARDS; i++) { - if (cy_card[i].base_addr != 0) - { - free_irq(cy_card[i].irq,NULL); - } + if (cy_card[i].base_addr != 0 + && cy_card[i].irq) + { + free_irq(cy_card[i].irq,NULL); + } } -} -#endif - -/* - * --------------------------------------------------------------------- - * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. - * sets global variables and return the number of ISA boards found. - * --------------------------------------------------------------------- - */ -int -cy_detect_isa() -{ - unsigned int cy_isa_irq,nboard; - unsigned char *cy_isa_address; - unsigned short i,j,cy_isa_nchan; - - nboard = 0; - - /* scan the address table probing for Cyclom-Y/ISA boards */ - for (i = 0 ; i < NR_ISA_ADDRESSES ; i++) { - cy_isa_address = cy_isa_addresses[i]; - if (cy_isa_address == 0x0000) { - return(nboard); - } - - /* probe for CD1400... */ - cy_isa_nchan = 4 * cy_init_card(cy_isa_address,0); - if (cy_isa_nchan == 0) { - continue; - } - - /* find out the board's irq by probing */ - cy_isa_irq = do_auto_irq(cy_isa_address); - if (cy_isa_irq == 0) { - printk("Cyclom-Y/ISA found at 0x%x but the IRQ could not be detected.\n", - (unsigned int) cy_isa_address); - continue; - } - - if((cy_next_channel+cy_isa_nchan) > NR_PORTS) { - printk("Cyclom-Y/ISA found at 0x%x but no more channel structures are available.\n", - (unsigned int) cy_isa_address); - return(nboard); - } - /* fill the next cy_card structure available */ - for (j = 0 ; j < NR_CARDS ; j++) { - if (cy_card[j].base_addr == 0) break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/ISA found at 0x%x but no more card structures are available.\n", - (unsigned int) cy_isa_address); - return(nboard); - } - - /* allocate IRQ */ - if(request_irq(cy_isa_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL)) - { - printk("Cyclom-Y/ISA found at 0x%x but could not allocate interrupt IRQ#%d.\n", - (unsigned int) cy_isa_address,cy_isa_irq); - return(nboard); - } - - /* set cy_card */ - cy_card[j].base_addr = (int) cy_isa_address; - cy_card[j].irq = (int) cy_isa_irq; - cy_card[j].bus_index = 0; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_isa_nchan/4; - IRQ_cards[cy_isa_irq] = &cy_card[j]; - nboard++; - - /* print message */ - printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n", - j+1,(unsigned int) cy_isa_address, - (unsigned int)(cy_isa_address + 0x1fff), - cy_isa_irq,cy_isa_nchan,cy_next_channel); - cy_next_channel += cy_isa_nchan; - } - return(nboard); - -} - -/* - * --------------------------------------------------------------------- - * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI. - * sets global variables and return the number of PCI boards found. - * --------------------------------------------------------------------- - */ -int -cy_detect_pci() +} /* cleanup_module */ +#else +/* called by linux/init/main.c to parse command line options */ +void +cy_setup(char *str, int *ints) { -#ifdef CONFIG_PCI - unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; - unsigned long pci_intr_ctrl; - unsigned char cy_pci_irq; - unsigned int cy_pci_address, cy_pci_io; - unsigned short i,j,cy_pci_nchan; - unsigned short device_id,dev_index = 0,board_index = 0; - - if(pcibios_present() == 0) { /* PCI bus not present */ - return(0); - } - for (i = 0; i < NR_CARDS; i++) { - /* look for a Cyclom-Y card by vendor and device id */ - while((device_id = cy_pci_dev_id[dev_index]) != 0) { - if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, - device_id,board_index, - &cyy_bus, &cyy_dev_fn) != 0) - { - dev_index++; /* try next device id */ - board_index = 0; - } else { - board_index++; - break; /* found a board */ - } - } - if (device_id == 0) break; - - /* read PCI configuration area */ - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_INTERRUPT_LINE, &cy_pci_irq); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_1, &cy_pci_io); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_2, &cy_pci_address); - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_REVISION_ID, &cyy_rev_id); - cy_pci_address &= 0xfffffff0; - if ((ulong)cy_pci_address >= 0x100000) { /* above 1M? */ - cy_pci_address = - (unsigned int) vremap(cy_pci_address,0x4000); - } - cy_pci_io &= 0xfffffffc; - cy_pci_nchan = 4 * cy_init_card((unsigned char *) - cy_pci_address,1); - if(cy_pci_nchan == 0) { - printk("Cyclom-Y PCI host card with no Serial-Modules at 0x%x.\n", - (unsigned int) cy_pci_address); - continue; - } - if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { - printk("Cyclom-Y/PCI found at 0x%x but no more channel structures are available.\n", - (unsigned int) cy_pci_address); - return(i); - } -#ifdef CY_PCI_DEBUG - printk("Cyclom-Ye/PCI #%d (bus=0x0%x, pci_id=0x%x, rev_id=%d).\n", - i+1,cyy_bus,cyy_dev_fn,cyy_rev_id); - printk("Cyclom-Ye/PCI: found at 0x%x, IRQ%d, ioaddr = 0x%lx.\n", - cy_pci_address,(int)cy_pci_irq,cy_pci_io); -#endif - /* fill the next cy_card structure available */ - for (j = 0 ; j < NR_CARDS ; j++) { - if (cy_card[j].base_addr == 0) break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/PCI found at 0x%x but no more card structures are available.\n", - (unsigned int) cy_pci_address); - return(i); - } - - /* allocate IRQ */ - if(request_irq(cy_pci_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL)) - { - printk("Cyclom-Y/PCI found at 0x%x but could not allocate interrupt IRQ%d.\n", - (unsigned int) cy_pci_address,cy_pci_irq); - return(i); - } + int i, j; - /* set cy_card */ - cy_card[j].base_addr = (int) cy_pci_address; - cy_card[j].irq = (int) cy_pci_irq; - cy_card[j].bus_index = 1; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_pci_nchan/4; - IRQ_cards[cy_pci_irq] = &cy_card[j]; - - /* enable interrupts in the PCI interface */ - outw(inw(cy_pci_io+0x68)|0x0900,cy_pci_io+0x68); - pci_intr_ctrl = (unsigned long)(inw(cy_pci_io+0x68) | inw(cy_pci_io+0x6a)<<16); - - /* print message */ - printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n", - j+1,(unsigned int) cy_pci_address, - (unsigned int)(cy_pci_address + 0x3fff), - (int)cy_pci_irq,cy_pci_nchan,cy_next_channel); + for (i = 0 ; i < NR_ISA_ADDRS ; i++) { + if (cy_isa_addresses[i] == 0) break; + } + for (j = 1; j <= ints[0]; j++){ + if ( i < NR_ISA_ADDRS ){ + cy_isa_addresses[i++] = (unsigned char *)(ints[j]); + } + } - cy_next_channel += cy_pci_nchan; - } - return(i); -#else - return(0); -#endif /* ifdef CONFIG_PCI */ -} +} /* cy_setup */ +#endif -#ifdef CYCLOM_SHOW_STATUS +#ifdef CY_SHOW_STATUS static void show_status(int line_num) { @@ -3183,7 +5537,8 @@ printk(" cy_port\n"); printk(" card line flags = %d %d %x\n", info->card, info->line, info->flags); - printk(" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n", + printk(" *tty read_status_mask timeout xmit_fifo_size ", + printk("= %lx %x %x %x\n", (long)info->tty, info->read_status_mask, info->timeout, info->xmit_fifo_size); printk(" cor1,cor2,cor3,cor4,cor5 = %x %x %x %x %x\n", @@ -3200,59 +5555,60 @@ save_flags(flags); cli(); - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int isicom_refcount = 0; +static int prev_card = 3; /* start servicing isi_card[0] */ +static struct isi_board * irq_to_board[16] = { NULL, }; +static struct tty_driver isicom_normal, isicom_callout; +static struct tty_struct * isicom_table[PORT_COUNT] = { NULL, }; +static struct termios * isicom_termios[PORT_COUNT] = { NULL, }; +static struct termios * isicom_termios_locked[PORT_COUNT] = { NULL, }; + +static struct isi_board isi_card[BOARD_COUNT]; +static struct isi_port isi_ports[PORT_COUNT]; + +DECLARE_TASK_QUEUE(tq_isicom); + +static struct timer_list tx; +static char re_schedule = 1; +#ifdef ISICOM_DEBUG +unsigned long tx_count = 0; +#endif + +static int ISILoad_open(struct inode *inode, struct file *filp); +static void ISILoad_release(struct inode *inode, struct file *filp); +static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); + +static void isicom_tx(unsigned long _data); +static void isicom_start(struct tty_struct * tty); + +static unsigned char * tmp_buf = 0; +static struct semaphore tmp_buf_sem = MUTEX; + +/* baud index mappings from linux defns to isi */ + +static char linuxb_to_isib[] = { + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, + 18, 19 +}; + +/* + * Firmware loader driver specific routines + * + */ + +static struct file_operations ISILoad_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + ISILoad_ioctl, + NULL, /* mmap */ + ISILoad_open, + ISILoad_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ +}; + +struct miscdevice isiloader_device = { + ISILOAD_MISC_MINOR, "isictl", &ISILoad_fops +}; + + +extern inline int WaitTillCardIsFree(unsigned short base) +{ + unsigned long count=0; + while( (!(inw(base+0xe) & 0x1)) && (count++ < 6000000)); + if (inw(base+0xe)&0x1) + return 0; + else + return 1; +} + +static int ISILoad_open(struct inode *inode, struct file *filp) +{ +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISILoad:Firmware loader Opened!!!\n"); +#endif + return 0; +} + +static void ISILoad_release(struct inode *inode, struct file *filp) +{ +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISILoad:Firmware loader Close(Release)d\n",); +#endif +} + +static int ISILoad_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + unsigned int card, i, j, signature, status; + unsigned short error, word_count, base; + bin_frame frame; + /* exec_record exec_rec; */ + + /* Added this check to avoid oopses on an ioctl with no + * args - sameer + */ + error=verify_area(VERIFY_READ, (void *) arg, sizeof(int)); + if (error) + return error; + card=get_user((int *)arg); + if(card < 0 || card >= BOARD_COUNT) + return -ENXIO; + + base=isi_card[card].base; + + if(base==0) + return -ENXIO; /* disabled or not used */ + + switch(cmd) { + case MIOCTL_RESET_CARD: + if (!suser()) + return -EPERM; + error=verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); + if (error) + return error; + + printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%x ",card+1,base); + + inw(base+0x8); + + for(i=jiffies+HZ/100;i>jiffies;); + + outw(0,base+0x8); /* Reset */ + + for(j=1;j<=3;j++) { + for(i=jiffies+HZ;i>jiffies;); + printk("."); + } + signature=(inw(base+0x4)) & 0xff; + + if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) { +#ifdef ISICOM_DEBUG + printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe)); +#endif + printk("\nISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base); + return -EIO; + } + + switch(signature) { + case 0xa5: + case 0xbb: + case 0xdd: isi_card[card].port_count = 8; + isi_card[card].shift_count = 12; + break; + + case 0xcc: isi_card[card].port_count = 16; + isi_card[card].shift_count = 11; + break; + + default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base); +#ifdef ISICOM_DEBUG + printk("Sig=0x%x\n",signature); +#endif + return -EIO; + } + printk("-Done\n"); + put_user(signature,(unsigned int*)arg); + return 0; + + case MIOCTL_LOAD_FIRMWARE: + if (!suser()) + return -EPERM; + error=verify_area(VERIFY_READ, (void *) arg, sizeof(bin_frame)); + if (error) + return error; + + memcpy_fromfs(&frame, (void *) arg, sizeof(bin_frame)); + + if (WaitTillCardIsFree(base)) + return -EIO; + + outw(0xf0,base); /* start upload sequence */ + outw(0x00,base); + outw((frame.addr), base);/* lsb of adderess */ + + word_count=(frame.count >> 1) + frame.count % 2; + outw(word_count, base); + InterruptTheCard(base); + + for(i=0;i<=0x2f;i++); /* a wee bit of delay */ + + if (WaitTillCardIsFree(base)) + return -EIO; + + if ((status=inw(base+0x4))!=0) { + printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", + card+1, frame.addr, frame.count, status); + return -EIO; + } + outsw(base, (void *) frame.bin_data, word_count); + + InterruptTheCard(base); + + for(i=0;i<=0x0f;i++); /* another wee bit of delay */ + + if (WaitTillCardIsFree(base)) + return -EIO; + + if ((status=inw(base+0x4))!=0) { + printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status); + return -EIO; + } + return 0; + + case MIOCTL_READ_FIRMWARE: + if (!suser()) + return -EPERM; + error=verify_area(VERIFY_READ, (void *) arg, sizeof(bin_header)); + if (error) + return error; + + memcpy_fromfs(&frame, (void *) arg, sizeof(bin_header)); + + if (WaitTillCardIsFree(base)) + return -EIO; + + outw(0xf1,base); /* start download sequence */ + outw(0x00,base); + outw((frame.addr), base);/* lsb of adderess */ + + word_count=(frame.count >> 1) + frame.count % 2; + outw(word_count+1, base); + InterruptTheCard(base); + + for(i=0;i<=0xf;i++); /* a wee bit of delay */ + + if (WaitTillCardIsFree(base)) + return -EIO; + + if ((status=inw(base+0x4))!=0) { + printk(KERN_WARNING "ISILoad:Card%d rejected verify header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", + card+1, frame.addr, frame.count, status); + return -EIO; + } + + inw(base); + insw(base, frame.bin_data, word_count); + InterruptTheCard(base); + + for(i=0;i<=0x0f;i++); /* another wee bit of delay */ + + if (WaitTillCardIsFree(base)) + return -EIO; + + if ((status=inw(base+0x4))!=0) { + printk(KERN_ERR "ISILoad:Card%d verify got out of sync.Card Status:0x%x\n",card+1, status); + return -EIO; + } + error=verify_area(VERIFY_WRITE, (void *) arg, sizeof(bin_frame)); + if (error) + return error; + memcpy_tofs((void *) arg, &frame, sizeof(bin_frame)); + + return 0; + + case MIOCTL_XFER_CTRL: + if (!suser()) + return -EPERM; + if (WaitTillCardIsFree(base)) + return -EIO; + + outw(0xf2, base); + outw(0x800, base); + outw(0x0, base); + outw(0x0, base); + InterruptTheCard(base); + + isi_card[card].status |= FIRMWARE_LOADED; + return 0; + + default: +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISILoad: Received Ioctl cmd 0x%x.\n", cmd); +#endif + return -ENOIOCTLCMD; + + } + +} + + +/* + * ISICOM Driver specific routines ... + * + */ + +static inline int isicom_paranoia_check(struct isi_port const * port, kdev_t dev, + const char * routine) +{ +#ifdef ISICOM_DEBUG + static const char * badmagic = + KERN_WARNING "ISICOM: Warning: bad isicom magic for dev %s in %s.\n"; + static const char * badport = + KERN_WARNING "ISICOM: Warning: NULL isicom port for dev %s in %s.\n"; + if (!port) { + printk(badport, kdevname(dev), routine); + return 1; + } + if (port->magic != ISICOM_MAGIC) { + printk(badmagic, kdevname(dev), routine); + return 1; + } +#endif + return 0; +} + +extern inline void schedule_bh(struct isi_port * port) +{ + queue_task_irq_off(&port->bh_tqueue, &tq_isicom); + mark_bh(ISICOM_BH); +} + +/* Transmitter */ + +static void isicom_tx(unsigned long _data) +{ + short count = (BOARD_COUNT-1), card, base; + short txcount, wait, wrd, residue, word_count, cnt; + struct isi_port * port; + struct tty_struct * tty; + unsigned long flags; + +#ifdef ISICOM_DEBUG + ++tx_count; +#endif + + /* find next active board */ + card = (prev_card + 1) & 0x0003; + while(count-- > 0) { + if (isi_card[card].status & BOARD_ACTIVE) + break; + card = (card + 1) & 0x0003; + } + if (!(isi_card[card].status & BOARD_ACTIVE)) + goto sched_again; + + prev_card = card; + + count = isi_card[card].port_count; + port = isi_card[card].ports; + base = isi_card[card].base; + for (;count > 0;count--, port++) { + /* port not active or tx disabled to force flow control */ + if (!(port->status & ISI_TXOK)) + continue; + + tty = port->tty; + save_flags(flags); cli(); + txcount = MIN(TX_SIZE, port->xmit_cnt); + if ((txcount <= 0) || tty->stopped || tty->hw_stopped) { + restore_flags(flags); + continue; + } + wait = 200; + while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); + if (wait <= 0) { + restore_flags(flags); +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: isicom_tx:Card(0x%x) found busy.\n", + card); +#endif + continue; + } + if (!(inw(base + 0x02) & (1 << port->channel))) { + restore_flags(flags); +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: isicom_tx: cannot tx to 0x%x:%d.\n", + base, port->channel + 1); +#endif + continue; + } +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: txing %d bytes, port%d.\n", + txcount, port->channel+1); +#endif + outw((port->channel << isi_card[card].shift_count) | txcount + , base); + residue = NO; + wrd = 0; + while (1) { + cnt = MIN(txcount, (SERIAL_XMIT_SIZE - port->xmit_tail)); + if (residue == YES) { + residue = NO; + if (cnt > 0) { + wrd |= (port->xmit_buf[port->xmit_tail] << 8); + port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt--; + txcount--; + cnt--; + outw(wrd, base); + } + else { + outw(wrd, base); + break; + } + } + if (cnt <= 0) break; + word_count = cnt >> 1; + outsw(base, port->xmit_buf+port->xmit_tail, word_count); + port->xmit_tail = (port->xmit_tail + (word_count << 1)) & + (SERIAL_XMIT_SIZE - 1); + txcount -= (word_count << 1); + port->xmit_cnt -= (word_count << 1); + if (cnt & 0x0001) { + residue = YES; + wrd = port->xmit_buf[port->xmit_tail]; + port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt--; + txcount--; + } + } +/* + * Replaced the code below with hopefully a faster loop - sameer + */ + +/* + while (1) { + wrd = port->xmit_buf[port->xmit_tail++]; + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt--; + if (--txcount > 0) { + wrd |= (port->xmit_buf[port->xmit_tail++] << 8); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt--; + outw(wrd, base); + if (--txcount <= 0) break; + } + else { + outw(wrd, base); + break; + } + } +*/ + InterruptTheCard(base); + if (port->xmit_cnt <= 0) + port->status &= ~ISI_TXOK; + if (port->xmit_cnt <= WAKEUP_CHARS) + schedule_bh(port); + restore_flags(flags); + } + + /* schedule another tx for hopefully in about 10ms */ +sched_again: + if (!re_schedule) + return; + init_timer(&tx); + tx.expires = jiffies + HZ/100; + tx.data = 0; + tx.function = isicom_tx; + add_timer(&tx); + + return; +} + + /* Interrupt handlers */ +static void do_isicom_bh(void) +{ + run_task_queue(&tq_isicom); +} + + + +static void isicom_bottomhalf(void * data) +{ + struct isi_port * port = (struct isi_port *) data; + struct tty_struct * tty = port->tty; + + if (!tty) + return; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); +} + +/* main interrupt handler routine */ +static void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct isi_board * card; + struct isi_port * port; + struct tty_struct * tty; + unsigned short base, header, word_count, count; + unsigned char channel; + short byte_count; + + card = irq_to_board[irq]; + if (!card || !(card->status & FIRMWARE_LOADED)) { + printk(KERN_DEBUG "ISICOM: interrupt: not handling irq%d!.\n", irq); + return; + } + base = card->base; + + inw(base); /* get the dummy word out */ + header = inw(base); + channel = (header & 0x7800) >> card->shift_count; + byte_count = header & 0xff; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM:Intr:(0x%x:%d).\n", base, channel+1); +#endif + if ((channel+1) > card->port_count) { + printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%x): %d(channel) > port_count.\n", + base, channel+1); + ClearInterrupt(base); + return; + } + port = card->ports + channel; + if (!(port->flags & ASYNC_INITIALIZED)) { + ClearInterrupt(base); + return; + } + + tty = port->tty; + + if (header & 0x8000) { /* Status Packet */ + header = inw(base); + switch(header & 0xff) { + case 0: /* Change in EIA signals */ + + if (port->flags & ASYNC_CHECK_CD) { + if (port->status & ISI_DCD) { + if (!(header & ISI_DCD)) { + /* Carrier has been lost */ +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: interrupt: DCD->low.\n"); +#endif + port->status &= ~ISI_DCD; + if (!((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_CALLOUT_NOHUP))) + queue_task_irq_off(&port->hangup_tq, + &tq_scheduler); + } + } + else { + if (header & ISI_DCD) { + /* Carrier has been detected */ +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: interrupt: DCD->high.\n"); +#endif + port->status |= ISI_DCD; + wake_up_interruptible(&port->open_wait); + } + } + } + else { + if (header & ISI_DCD) + port->status |= ISI_DCD; + else + port->status &= ~ISI_DCD; + } + + if (port->flags & ASYNC_CTS_FLOW) { + if (port->tty->hw_stopped) { + if (header & ISI_CTS) { + port->tty->hw_stopped = 0; + /* start tx ing */ + port->status |= (ISI_TXOK | ISI_CTS); + schedule_bh(port); + } + } + else { + if (!(header & ISI_CTS)) { + port->tty->hw_stopped = 1; + /* stop tx ing */ + port->status &= ~(ISI_TXOK | ISI_CTS); + } + } + } + else { + if (header & ISI_CTS) + port->status |= ISI_CTS; + else + port->status &= ~ISI_CTS; + } + + if (header & ISI_DSR) + port->status |= ISI_DSR; + else + port->status &= ~ISI_DSR; + + if (header & ISI_RI) + port->status |= ISI_RI; + else + port->status &= ~ISI_RI; + + break; + + case 1: /* Received Break !!! */ + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + /* dunno if this is right */ + *tty->flip.char_buf_ptr++ = 0; + tty->flip.count++; + if (port->flags & ASYNC_SAK) + do_SAK(tty); + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + break; + + case 2: /* Statistics */ + printk(KERN_DEBUG "ISICOM: isicom_interrupt: stats!!!.\n"); + break; + + default: + printk(KERN_WARNING "ISICOM: Intr: Unknown code in status packet.\n"); + break; + } + } + else { /* Data Packet */ + count = MIN(byte_count, (TTY_FLIPBUF_SIZE - tty->flip.count)); +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: Intr: Can rx %d of %d bytes.\n", + count, byte_count); +#endif + word_count = count >> 1; + insw(base, tty->flip.char_buf_ptr, word_count); + tty->flip.char_buf_ptr += (word_count << 1); + byte_count -= (word_count << 1); + if (count & 0x0001) { + *tty->flip.char_buf_ptr++ = (char)(inw(base) & 0xff); + byte_count -= 2; + } + memset(tty->flip.flag_buf_ptr, 0, count); + tty->flip.flag_buf_ptr += count; + tty->flip.count += count; + + if (byte_count > 0) { + printk(KERN_DEBUG "ISICOM: Intr(0x%x:%d): Flip buffer overflow! dropping bytes...\n", + base, channel+1); + while(byte_count > 0) { /* drain out unread xtra data */ + inw(base); + byte_count -= 2; + } + } + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + } + ClearInterrupt(base); + return; +} + + /* called with interrupts disabled */ +static void isicom_config_port(struct isi_port * port) +{ + struct isi_board * card = port->card; + struct tty_struct * tty; + unsigned long baud; + unsigned short channel_setup, wait, base = card->base; + unsigned short channel = port->channel, shift_count = card->shift_count; + unsigned char flow_ctrl; + + if (!(tty = port->tty) || !tty->termios) + return; + baud = C_BAUD(tty); + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + + /* if CBAUDEX bit is on and the baud is set to either 50 or 75 + * then the card is programmed for 57.6Kbps or 115Kbps + * respectively. + */ + + if (baud < 1 || baud > 2) + port->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15) { + + /* the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set + * by the set_serial_info ioctl ... this is done by + * the 'setserial' utility. + */ + + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baud++; /* 57.6 Kbps */ + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baud +=2; /* 115 Kbps */ + } + if (linuxb_to_isib[baud] == -1) { + /* hang up */ + drop_dtr(port); + return; + } + else + raise_dtr(port); + + wait = 100; + while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0)); + if (!wait) { + printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at channel setup.\n"); + return; + } + outw(0x8000 | (channel << shift_count) |0x03, base); + outw(linuxb_to_isib[baud] << 8 | 0x03, base); + channel_setup = 0; + switch(C_CSIZE(tty)) { + case CS5: + channel_setup |= ISICOM_CS5; + break; + case CS6: + channel_setup |= ISICOM_CS6; + break; + case CS7: + channel_setup |= ISICOM_CS7; + break; + case CS8: + channel_setup |= ISICOM_CS8; + break; + } + + if (C_CSTOPB(tty)) + channel_setup |= ISICOM_2SB; + + if (C_PARENB(tty)) + channel_setup |= ISICOM_EVPAR; + if (C_PARODD(tty)) + channel_setup |= ISICOM_ODPAR; + outw(channel_setup, base); + InterruptTheCard(base); + + if (C_CLOCAL(tty)) + port->flags &= ~ASYNC_CHECK_CD; + else + port->flags |= ASYNC_CHECK_CD; + + /* flow control settings ...*/ + flow_ctrl = 0; + port->flags &= ~ASYNC_CTS_FLOW; + if (C_CRTSCTS(tty)) { + port->flags |= ASYNC_CTS_FLOW; + flow_ctrl |= ISICOM_CTSRTS; + } + if (I_IXON(tty)) + flow_ctrl |= ISICOM_RESPOND_XONXOFF; + if (I_IXOFF(tty)) + flow_ctrl |= ISICOM_INITIATE_XONXOFF; + + wait = 100; + while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0)); + if (!wait) { + printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at flow setup.\n"); + return; + } + outw(0x8000 | (channel << shift_count) |0x04, base); + outw(flow_ctrl << 8 | 0x05, base); + outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); + InterruptTheCard(base); + + /* rx enabled -> enable port for rx on the card */ + if (C_CREAD(tty)) { + card->port_status |= (1 << channel); + outw(card->port_status, base + 0x02); + } + +} + +/* open et all */ + +extern inline void isicom_setup_board(struct isi_board * bp) +{ + int channel; + struct isi_port * port; + unsigned long flags; + + if (bp->status & BOARD_ACTIVE) + return; + port = bp->ports; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts start, port_count %d...\n", bp->port_count); +#endif + for(channel = 0; channel < bp->port_count; channel++, port++) { + save_flags(flags); cli(); + drop_dtr_rts(port); + restore_flags(flags); + } +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts stop...\n"); +#endif + + bp->status |= BOARD_ACTIVE; + MOD_INC_USE_COUNT; + return; +} + +static int isicom_setup_port(struct isi_port * port) +{ + struct isi_board * card = port->card; + unsigned long flags; + + if (port->flags & ASYNC_INITIALIZED) + return 0; + if (!port->xmit_buf) { + unsigned long page; + + if (!(page = get_free_page(GFP_KERNEL))) + return -ENOMEM; + + if (port->xmit_buf) { + free_page(page); + return -ERESTARTSYS; + } + port->xmit_buf = (unsigned char *) page; + } + save_flags(flags); cli(); + if (port->tty) + clear_bit(TTY_IO_ERROR, &port->tty->flags); + if (port->count == 1) + card->count++; + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + + /* discard any residual data */ + kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX); + + isicom_config_port(port); + port->flags |= ASYNC_INITIALIZED; + + restore_flags(flags); + + return 0; +} + +static int block_til_ready(struct tty_struct * tty, struct file * filp, struct isi_port * port) +{ + int do_clocal = 0, retval; + struct wait_queue wait = { current, NULL }; + + /* block if port is in the process of being closed */ + + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: block_til_ready: close in progress.\n"); +#endif + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + /* trying to open a callout device... check for constraints */ + + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: bl_ti_rdy: callout open.\n"); +#endif + if (port->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_SESSION_LOCKOUT) && + (port->session != current->session)) + return -EBUSY; + + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_PGRP_LOCKOUT) && + (port->pgrp != current->pgrp)) + return -EBUSY; + port->flags |= ASYNC_CALLOUT_ACTIVE; + cli(); + raise_dtr_rts(port); + sti(); + return 0; + } + + /* if non-blocking mode is set ... */ + + if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: block_til_ready: non-block mode.\n"); +#endif + if (port->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (port->flags & ASYNC_CALLOUT_ACTIVE) { + if (port->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (C_CLOCAL(tty)) + do_clocal = 1; + } +#ifdef ISICOM_DEBUG + if (do_clocal) + printk(KERN_DEBUG "ISICOM: block_til_ready: CLOCAL set.\n"); +#endif + + /* block waiting for DCD to be asserted, and while + callout dev is busy */ + retval = 0; + add_wait_queue(&port->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + port->count--; + sti(); + port->blocked_open++; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: block_til_ready: waiting for DCD...\n"); +#endif + while (1) { + cli(); + if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) + raise_dtr_rts(port); + + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: block_til_ready: tty_hung_up_p || not init.\n"); +#endif + break; + } + if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && + !(port->flags & ASYNC_CLOSING) && + (do_clocal || (port->status & ISI_DCD))) { +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: block_til_ready: do_clocal || DCD.\n"); +#endif + break; + } + if (current->signal & ~current->blocked) { +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: block_til_ready: sig blocked.\n"); +#endif + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&port->open_wait, &wait); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + if (retval) + return retval; + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int isicom_open(struct tty_struct * tty, struct file * filp) +{ + struct isi_port * port; + struct isi_board * card; + unsigned int line, board; + unsigned long flags; + int error; + +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: open start!!!.\n"); +#endif + line = MINOR(tty->device) - tty->driver.minor_start; + +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "line = %d.\n", line); +#endif + + if ((line < 0) || (line > (PORT_COUNT-1))) + return -ENODEV; + board = BOARD(line); + +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "board = %d.\n", board); +#endif + + card = &isi_card[board]; + if (!(card->status & FIRMWARE_LOADED)) { +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG"ISICOM: Firmware not loaded to card%d.\n", board); +#endif + return -ENODEV; + } + + /* open on higher 8 dev files on a 8 port card !!! */ + if (card->port_count == 8) + if (line > ((board * 16)+7)) { + printk(KERN_ERR "ISICOM: Opened >8 on a 8 port card.\n"); + return -ENODEV; + } + port = &isi_ports[line]; + if (isicom_paranoia_check(port, tty->device, "isicom_open")) + return -ENODEV; + +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: isicom_setup_board ...\n"); +#endif + isicom_setup_board(card); + + port->count++; + tty->driver_data = port; + port->tty = tty; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: isicom_setup_port ...\n"); +#endif + if ((error = isicom_setup_port(port))!=0) + return error; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: block_til_ready ...\n"); +#endif + if ((error = block_til_ready(tty, filp, port))!=0) + return error; + + if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = port->normal_termios; + else + *tty->termios = port->callout_termios; + save_flags(flags); cli(); + isicom_config_port(port); + restore_flags(flags); + } + + port->session = current->session; + port->pgrp = current->pgrp; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: open end!!!.\n"); +#endif + return 0; +} + +/* close et all */ + +extern inline void isicom_shutdown_board(struct isi_board * bp) +{ + int channel; + struct isi_port * port; + + if (!(bp->status & BOARD_ACTIVE)) + return; + bp->status &= ~BOARD_ACTIVE; + port = bp->ports; + for(channel = 0; channel < bp->port_count; channel++, port++) { + drop_dtr_rts(port); + } + MOD_DEC_USE_COUNT; +} + +static void isicom_shutdown_port(struct isi_port * port) +{ + struct isi_board * card = port->card; + struct tty_struct * tty; + + if (!(port->flags & ASYNC_INITIALIZED)) + return; + if (port->xmit_buf) { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + if (!(tty = port->tty) || C_HUPCL(tty)) + /* drop dtr on this port */ + drop_dtr(port); + + /* any other port uninits */ + + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + port->flags &= ~ASYNC_INITIALIZED; + + if (--card->count < 0) { + printk(KERN_DEBUG "ISICOM: isicom_shutdown_port: bad board(0x%x) count %d.\n", + card->base, card->count); + card->count = 0; + } + + /* last port was closed , shutdown that boad too */ + if (!card->count) + isicom_shutdown_board(card); +} + +static void isicom_close(struct tty_struct * tty, struct file * filp) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + struct isi_board * card = port->card; + unsigned long flags; + + if (!port) + return; + if (isicom_paranoia_check(port, tty->device, "isicom_close")) + return; + +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: Close start!!!.\n"); +#endif + + save_flags(flags); cli(); + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + if ((tty->count == 1) && (port->count != 1)) { + printk(KERN_WARNING "ISICOM:(0x%x) isicom_close: bad port count" + "tty->count = 1 port count = %d.\n", + card->base, port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_WARNING "ISICOM:(0x%x) isicom_close: bad port count for" + "channel%d = %d", card->base, port->channel, + port->count); + port->count = 0; + } + + if (port->count) { + restore_flags(flags); + return; + } + port->flags |= ASYNC_CLOSING; + /* + * save termios struct since callout and dialin termios may be + * different. + */ + if (port->flags & ASYNC_NORMAL_ACTIVE) + port->normal_termios = *tty->termios; + if (port->flags & ASYNC_CALLOUT_ACTIVE) + port->callout_termios = *tty->termios; + + tty->closing = 1; + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); + /* indicate to the card that no more data can be received + on this port */ + if (port->flags & ASYNC_INITIALIZED) { + card->port_status &= ~(1 << port->channel); + outw(card->port_status, card->base + 0x02); + } + isicom_shutdown_port(port); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + port->tty = 0; + if (port->blocked_open) { + if (port->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->close_delay; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: scheduling until time out.\n"); +#endif + schedule(); + } + wake_up_interruptible(&port->open_wait); + } + port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE | + ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); + restore_flags(flags); +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: Close end!!!.\n"); +#endif +} + +/* write et all */ +static int isicom_write(struct tty_struct * tty, int from_user, + const unsigned char * buf, int count) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + unsigned long flags; + int cnt, total = 0; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: isicom_write for port%d: %d bytes.\n", + port->channel+1, count); +#endif + if (isicom_paranoia_check(port, tty->device, "isicom_write")) + return 0; + + if (!tty || !port->xmit_buf || !tmp_buf) + return 0; + if (from_user) + down(&tmp_buf_sem); /* acquire xclusive access to tmp_buf */ + + save_flags(flags); + while(1) { + cli(); + cnt = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (cnt <= 0) + break; + + if (from_user) { + /* the following may block for paging... hence + enabling interrupts but tx routine may have + created more space in xmit_buf when the ctrl + gets back here + sti(); */ + memcpy_fromfs(tmp_buf, buf, cnt); +/* cli();*/ + cnt = MIN(cnt, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + memcpy(port->xmit_buf + port->xmit_head, tmp_buf, cnt); + } + else + memcpy(port->xmit_buf + port->xmit_head, buf, cnt); + port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt += cnt; + restore_flags(flags); + buf += cnt; + count -= cnt; + total += cnt; + } + if (from_user) + up(&tmp_buf_sem); + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped) + port->status |= ISI_TXOK; + restore_flags(flags); +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: isicom_write %d bytes written.\n", total); +#endif + return total; +} + +/* put_char et all */ +static void isicom_put_char(struct tty_struct * tty, unsigned char ch) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->device, "isicom_put_char")) + return; + + if (!tty || !port->xmit_buf) + return; +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: put_char, port %d, char %c.\n", port->channel+1, ch); +#endif + + save_flags(flags); cli(); + + if (port->xmit_cnt >= (SERIAL_XMIT_SIZE - 1)) { + restore_flags(flags); + return; + } + + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= (SERIAL_XMIT_SIZE - 1); + port->xmit_cnt++; + restore_flags(flags); +} + +/* flush_chars et all */ +static void isicom_flush_chars(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + + if (isicom_paranoia_check(port, tty->device, "isicom_flush_chars")) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->xmit_buf) + return; + + /* this tells the transmitter to consider this port for + data output to the card ... that's the best we can do. */ + port->status |= ISI_TXOK; +} + +/* write_room et all */ +static int isicom_write_room(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + int free; + if (isicom_paranoia_check(port, tty->device, "isicom_write_room")) + return 0; + + free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (free < 0) + free = 0; + return free; +} + +/* chars_in_buffer et all */ +static int isicom_chars_in_buffer(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + if (isicom_paranoia_check(port, tty->device, "isicom_chars_in_buffer")) + return 0; + return port->xmit_cnt; +} + +/* ioctl et all */ +extern inline void isicom_send_break(struct isi_port * port, unsigned long length) +{ + struct isi_board * card = port->card; + short wait = 10; + unsigned short base = card->base; + unsigned long flags; + + save_flags(flags); cli(); + while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0)); + if (!wait) { + printk(KERN_DEBUG "ISICOM: Card found busy in isicom_send_break.\n"); + return; + } + outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base); + outw((length & 0xff) << 8 | 0x00, base); + outw((length & 0xff00), base); + InterruptTheCard(base); + restore_flags(flags); +} + +static int isicom_get_modem_info(struct isi_port * port, unsigned int * value) +{ + /* just send the port status */ + unsigned int info; + unsigned short status = port->status; + + info = ((status & ISI_RTS) ? TIOCM_RTS : 0) | + ((status & ISI_DTR) ? TIOCM_DTR : 0) | + ((status & ISI_DCD) ? TIOCM_CAR : 0) | + ((status & ISI_DSR) ? TIOCM_DSR : 0) | + ((status & ISI_CTS) ? TIOCM_CTS : 0) | + ((status & ISI_RI ) ? TIOCM_RI : 0); + put_user(info, (unsigned long *) value); + return 0; +} + +static int isicom_set_modem_info(struct isi_port * port, unsigned int cmd, + unsigned int * value) +{ + unsigned int arg; + unsigned long flags; + + arg = get_user(value); + save_flags(flags); cli(); + switch(cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + raise_rts(port); + if (arg & TIOCM_DTR) + raise_dtr(port); + break; + + case TIOCMBIC: + if (arg & TIOCM_RTS) + drop_rts(port); + if (arg & TIOCM_DTR) + drop_dtr(port); + break; + + case TIOCMSET: + if (arg & TIOCM_RTS) + raise_rts(port); + else + drop_rts(port); + + if (arg & TIOCM_DTR) + raise_dtr(port); + else + drop_dtr(port); + break; + + default: + restore_flags(flags); + return -EINVAL; + } + restore_flags(flags); + return 0; +} + +static int isicom_set_serial_info(struct isi_port * port, + struct serial_struct * info) +{ + struct serial_struct newinfo; + unsigned long flags; + int reconfig_port; + + memcpy_fromfs(&newinfo, info, sizeof(newinfo)); + reconfig_port = ((port->flags & ASYNC_SPD_MASK) != + (newinfo.flags & ASYNC_SPD_MASK)); + + if (!suser()) { + if ((newinfo.close_delay != port->close_delay) || + (newinfo.closing_wait != port->closing_wait) || + ((newinfo.flags & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) + return -EPERM; + port->flags = ((port->flags & ~ ASYNC_USR_MASK) | + (newinfo.flags & ASYNC_USR_MASK)); + } + else { + port->close_delay = newinfo.close_delay; + port->closing_wait = newinfo.closing_wait; + port->flags = ((port->flags & ~ASYNC_FLAGS) | + (newinfo.flags & ASYNC_FLAGS)); + } + if (reconfig_port) { + save_flags(flags); cli(); + isicom_config_port(port); + restore_flags(flags); + } + return 0; +} + +static int isicom_get_serial_info(struct isi_port * port, + struct serial_struct * info) +{ + struct serial_struct out_info; + + memset(&out_info, 0, sizeof(out_info)); +/* out_info.type = ? */ + out_info.line = port - isi_ports; + out_info.port = port->card->base; + out_info.irq = port->card->irq; + out_info.flags = port->flags; +/* out_info.baud_base = ? */ + out_info.close_delay = port->close_delay; + out_info.closing_wait = port->closing_wait; + memcpy_tofs(info, &out_info, sizeof(out_info)); + return 0; +} + +static int isicom_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + int retval, error; + + if (isicom_paranoia_check(port, tty->device, "isicom_ioctl")) + return -ENODEV; + + switch(cmd) { + case TCSBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + isicom_send_break(port, HZ/4); + return 0; + + case TCSBRKP: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + isicom_send_break(port, arg ? arg * (HZ/10) : HZ/4); + return 0; + + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + return 0; + + case TIOCSSOFTCAR: + error = verify_area(VERIFY_READ, (void *) arg, sizeof(long)); + if (error) + return error; + arg = get_user((unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + return isicom_get_modem_info(port, (unsigned int*) arg); + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + error = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + return isicom_set_modem_info(port, cmd, + (unsigned int *) arg); + + case TIOCGSERIAL: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct)); + if (error) + return error; + return isicom_get_serial_info(port, + (struct serial_struct *) arg); + + case TIOCSSERIAL: + error = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct serial_struct)); + if (error) + return error; + return isicom_set_serial_info(port, + (struct serial_struct *) arg); + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* set_termios et all */ +static void isicom_set_termios(struct tty_struct * tty, struct termios * old_termios) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->device, "isicom_set_termios")) + return; + + if (tty->termios->c_cflag == old_termios->c_cflag && + tty->termios->c_iflag == old_termios->c_iflag) + return; + + save_flags(flags); cli(); + isicom_config_port(port); + restore_flags(flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + isicom_start(tty); + } +} + +/* throttle et all */ +static void isicom_throttle(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + struct isi_board * card = port->card; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->device, "isicom_throttle")) + return; + + /* tell the card that this port cannot handle any more data for now */ + save_flags(flags); cli(); + card->port_status &= ~(1 << port->channel); + outw(card->port_status, card->base + 0x02); + restore_flags(flags); +} + +/* unthrottle et all */ +static void isicom_unthrottle(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + struct isi_board * card = port->card; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->device, "isicom_unthrottle")) + return; + + /* tell the card that this port is ready to accept more data */ + save_flags(flags); cli(); + card->port_status |= (1 << port->channel); + outw(card->port_status, card->base + 0x02); + restore_flags(flags); +} + +/* stop et all */ +static void isicom_stop(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + + if (isicom_paranoia_check(port, tty->device, "isicom_stop")) + return; + + /* this tells the transmitter not to consider this port for + data output to the card. */ + port->status &= ~ISI_TXOK; +} + +/* start et all */ +static void isicom_start(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + + if (isicom_paranoia_check(port, tty->device, "isicom_start")) + return; + + /* this tells the transmitter to consider this port for + data output to the card. */ + port->status |= ISI_TXOK; +} + +/* hangup et all */ +static void do_isicom_hangup(void * data) +{ + struct isi_port * port = (struct isi_port *) data; + struct tty_struct * tty; + + tty = port->tty; + if (!tty) + return; + + tty_hangup(tty); +} + +static void isicom_hangup(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + + if (isicom_paranoia_check(port, tty->device, "isicom_hangup")) + return; + + isicom_shutdown_port(port); + port->count = 0; + port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + port->tty = 0; + wake_up_interruptible(&port->open_wait); +} + +/* flush_buffer et all */ +static void isicom_flush_buffer(struct tty_struct * tty) +{ + struct isi_port * port = (struct isi_port *) tty->driver_data; + unsigned long flags; + + if (isicom_paranoia_check(port, tty->device, "isicom_flush_buffer")) + return; + + save_flags(flags); cli(); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + + +static int register_ioregion(void) +{ + int count, done=0; + for (count=0; count < BOARD_COUNT; count++ ) { + if (isi_card[count].base) { + if (check_region(isi_card[count].base,16)) { + printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x is busy. Card%d will be disabled.\n", + isi_card[count].base,isi_card[count].base+15,count+1); + isi_card[count].base=0; + } + else { + request_region(isi_card[count].base,16,ISICOM_NAME); +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x requested for Card%d.\n",isi_card[count].base,isi_card[count].base+15,count+1); +#endif + done++; + } + } + } + return done; +} + +static void unregister_ioregion(void) +{ + int count; + for (count=0; count < BOARD_COUNT; count++ ) + if (isi_card[count].base) { + release_region(isi_card[count].base,16); +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x released for Card%d.\n",isi_card[count].base,isi_card[count].base+15,count+1); +#endif + } +} + +static int register_drivers(void) +{ + int error; + + /* tty driver structure initialization */ + memset(&isicom_normal, 0, sizeof(struct tty_driver)); + isicom_normal.magic = TTY_DRIVER_MAGIC; + isicom_normal.name = "ttyM"; + isicom_normal.major = ISICOM_NMAJOR; + isicom_normal.minor_start = 0; + isicom_normal.num = PORT_COUNT; + isicom_normal.type = TTY_DRIVER_TYPE_SERIAL; + isicom_normal.subtype = SERIAL_TYPE_NORMAL; + isicom_normal.init_termios = tty_std_termios; + isicom_normal.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL |CLOCAL; + isicom_normal.flags = TTY_DRIVER_REAL_RAW; + isicom_normal.refcount = &isicom_refcount; + + isicom_normal.table = isicom_table; + isicom_normal.termios = isicom_termios; + isicom_normal.termios_locked = isicom_termios_locked; + + isicom_normal.open = isicom_open; + isicom_normal.close = isicom_close; + isicom_normal.write = isicom_write; + isicom_normal.put_char = isicom_put_char; + isicom_normal.flush_chars = isicom_flush_chars; + isicom_normal.write_room = isicom_write_room; + isicom_normal.chars_in_buffer = isicom_chars_in_buffer; + isicom_normal.ioctl = isicom_ioctl; + isicom_normal.set_termios = isicom_set_termios; + isicom_normal.throttle = isicom_throttle; + isicom_normal.unthrottle = isicom_unthrottle; + isicom_normal.stop = isicom_stop; + isicom_normal.start = isicom_start; + isicom_normal.hangup = isicom_hangup; + isicom_normal.flush_buffer = isicom_flush_buffer; + + /* callout device */ + + isicom_callout = isicom_normal; + isicom_callout.name = "cum"; + isicom_callout.major = ISICOM_CMAJOR; + isicom_callout.subtype = SERIAL_TYPE_CALLOUT; + + if ((error=tty_register_driver(&isicom_normal))!=0) { + printk(KERN_DEBUG "ISICOM: Couldn't register the dialin driver, error=%d\n", + error); + return error; + } + if ((error=tty_register_driver(&isicom_callout))!=0) { + tty_unregister_driver(&isicom_normal); + printk(KERN_DEBUG "ISICOM: Couldn't register the callout driver, error=%d\n", + error); + return error; + } + return 0; +} + +static void unregister_drivers(void) +{ + int error; + if ((error=tty_unregister_driver(&isicom_callout))!=0) + printk(KERN_DEBUG "ISICOM: couldn't unregister callout driver error=%d.\n",error); + if (tty_unregister_driver(&isicom_normal)) + printk(KERN_DEBUG "ISICOM: couldn't unregister normal driver error=%d.\n",error); +} + +static int register_isr(void) +{ + int count, done=0; + for (count=0; count < BOARD_COUNT; count++ ) { + if (isi_card[count].base) { + if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT, 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); + isi_card[count].base=0; + } + else { + printk(KERN_INFO "ISICOM: Card%d at 0x%x using irq %d.\n", + count+1, isi_card[count].base, isi_card[count].irq); + + irq_to_board[isi_card[count].irq]=&isi_card[count]; + done++; + } + } + } + return done; +} + +static void unregister_isr(void) +{ + int count; + for (count=0; count < BOARD_COUNT; count++ ) + if (isi_card[count].base) { + free_irq(isi_card[count].irq, NULL); +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: Irq %d released for Card%d.\n",isi_card[count].irq, count+1); +#endif + } +} + +static int isicom_init(void) +{ + int card, channel, base; + struct isi_port * port; + unsigned long page; + + if (!tmp_buf) { + page = get_free_page(GFP_KERNEL); + if (!page) { +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: Couldn't allocate page for tmp_buf.\n"); +#else + printk(KERN_ERR "ISICOM: Not enough memory...\n"); +#endif + return 0; + } + tmp_buf = (unsigned char *) page; + } + + if (!register_ioregion()) + { + printk(KERN_ERR "ISICOM: All required I/O space found busy.\n"); + free_page((unsigned long)tmp_buf); + return 0; + } + if (register_drivers()) + { + unregister_ioregion(); + free_page((unsigned long)tmp_buf); + return 0; + } + if (!register_isr()) + { + unregister_drivers(); + /* ioports already uregistered in register_isr */ + free_page((unsigned long)tmp_buf); + return 0; + } + + /* initialize bottom half */ + init_bh(ISICOM_BH, do_isicom_bh); + + + memset(isi_ports, 0, sizeof(isi_ports)); + for (card = 0; card < BOARD_COUNT; card++) { + port = &isi_ports[card * 16]; + isi_card[card].ports = port; + base = isi_card[card].base; + for (channel = 0; channel < 16; channel++, port++) { + port->magic = ISICOM_MAGIC; + port->card = &isi_card[card]; + port->channel = channel; + port->normal_termios = isicom_normal.init_termios; + port->callout_termios = isicom_callout.init_termios; + port->close_delay = 50 * HZ/100; + port->closing_wait = 3000 * HZ/100; + port->hangup_tq.routine = do_isicom_hangup; + port->hangup_tq.data = port; + port->bh_tqueue.routine = isicom_bottomhalf; + port->bh_tqueue.data = port; + port->status = 0; + + /* . . . */ + } + } + + return 1; +} + +/* + * Insmod can set static symbols so keep these static + */ + +static int ISIBase1=0, ISIBase2=0, ISIBase3=0, ISIBase4=0; +static int Irq1=0, Irq2=0, Irq3=0, Irq4=0; + +int init_module(void) +{ + int retval, card; + + isi_card[0].base=ISIBase1; + isi_card[1].base=ISIBase2; + isi_card[2].base=ISIBase3; + isi_card[3].base=ISIBase4; + + isi_card[0].irq=Irq1; + isi_card[1].irq=Irq2; + isi_card[2].irq=Irq3; + isi_card[3].irq=Irq4; + + for (card=0 ;card < BOARD_COUNT; card++) { + if (!((isi_card[card].irq==2)||(isi_card[card].irq==3)|| + (isi_card[card].irq==4)||(isi_card[card].irq==5)|| + (isi_card[card].irq==7)||(isi_card[card].irq==10)|| + (isi_card[card].irq==11)||(isi_card[card].irq==12)|| + (isi_card[card].irq==15))) { + + if (isi_card[card].base) { + printk(KERN_ERR "ISICOM: Irq %d unsupported. Disabling Card%d...\n", + isi_card[card].irq, card+1); + isi_card[card].base=0; + } + } + } + + if (!(isi_card[0].base || isi_card[1].base || isi_card[2].base || isi_card[3].base)) { + printk(KERN_ERR "ISICOM: No valid card configuration. Driver cannot be initialized...\n"); + return -EIO; + } + retval=misc_register(&isiloader_device); + if (retval<0) { + printk(KERN_ERR "ISICOM: Unable to register firmware loader driver.\n"); + return -EIO; + } + + if (!isicom_init()) { + if (misc_deregister(&isiloader_device)) + printk(KERN_ERR "ISICOM: Unable to unregister Firmware Loader driver\n"); + return -EIO; + } + + init_timer(&tx); + tx.expires = jiffies + 1; + tx.data = 0; + tx.function = isicom_tx; + re_schedule = 1; + add_timer(&tx); + + return 0; +} + +void cleanup_module(void) +{ + re_schedule = 0; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + disable_bh(ISICOM_BH); + +#ifdef ISICOM_DEBUG + printk("ISICOM: isicom_tx tx_count = %ld.\n", tx_count); +#endif + +#ifdef ISICOM_DEBUG + printk("ISICOM: uregistering isr ...\n"); +#endif + unregister_isr(); + +#ifdef ISICOM_DEBUG + printk("ISICOM: unregistering drivers ...\n"); +#endif + unregister_drivers(); + +#ifdef ISICOM_DEBUG + printk("ISICOM: unregistering ioregion ...\n"); +#endif + unregister_ioregion(); + +#ifdef ISICOM_DEBUG + printk("ISICOM: freeing tmp_buf ...\n"); +#endif + free_page((unsigned long)tmp_buf); + +#ifdef ISICOM_DEBUG + printk("ISICOM: unregistering firmware loader ...\n"); +#endif + if (misc_deregister(&isiloader_device)) + printk(KERN_ERR "ISICOM: Unable to unregister Firmware Loader driver\n"); +} diff -u --recursive --new-file v2.0.35/linux/drivers/char/lp.c linux/drivers/char/lp.c --- v2.0.35/linux/drivers/char/lp.c Sun Nov 15 10:49:34 1998 +++ linux/drivers/char/lp.c Sun Nov 15 10:32:54 1998 @@ -89,14 +89,18 @@ while(wait != LP_WAIT(minor)) wait++; /* control port takes strobe high */ outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor ))); - /* Wait until NBUSY line goes high */ - count = 0; - do { - status = LP_S(minor); - count++; - if (need_resched) - schedule(); - } while (LP_READY(minor, status) && (count #include #include - +#include +#include /* ************************************************************** */ /* * This section can be removed when 2.0 becomes outdated.... * */ @@ -170,7 +175,6 @@ #define SPECIALIX_TYPE_NORMAL 1 #define SPECIALIX_TYPE_CALLOUT 2 -static struct specialix_board * IRQ_to_board[16] = { NULL, } ; static struct tty_driver specialix_driver, specialix_callout_driver; static int specialix_refcount = 0; static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, }; @@ -322,23 +326,28 @@ extern inline int sx_check_io_range(struct specialix_board * bp) { - return check_region (bp->base, SX_IO_SPACE); + return check_region (bp->base, + bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE); } extern inline void sx_request_io_range(struct specialix_board * bp) { - request_region(bp->base, SX_IO_SPACE, "specialix IO8+" ); + request_region(bp->base, + bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE, + "specialix IO8+" ); } extern inline void sx_release_io_range(struct specialix_board * bp) { - release_region(bp->base, SX_IO_SPACE); + release_region(bp->base, + bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE); } /* Must be called with enabled interrupts */ +/* Ugly. Don't use this for anything else than initialization code */ extern inline void sx_long_delay(unsigned long delay) { unsigned long i; @@ -354,6 +363,8 @@ int virq; int i; + if (bp->flags & SX_BOARD_IS_PCI) + return 1; switch (bp->irq) { /* In the same order as in the docs... */ case 15: virq = 0;break; @@ -481,9 +492,14 @@ printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", board_No(bp), val1, val2); #endif - if (val1 != 0xb2) { - printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n", - board_No(bp), bp->base); + /* They managed to switch the bit order between the docs and + the IO8+ card. The new PCI card now conforms to old docs. + They changed the PCI docs to reflect the situation on the + old card. */ + val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2; + if (val1 != val2) { + printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n", + board_No(bp), val2, bp->base, val1); return 1; } @@ -865,7 +881,7 @@ unsigned long loop = 0; int saved_reg; - bp = IRQ_to_board[irq]; + bp = dev_id; if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) { #ifdef SPECIALIX_DEBUG @@ -921,6 +937,25 @@ * Routines for open & close processing. */ +void turn_ints_off (struct specialix_board *bp) +{ + if (bp->flags & SX_BOARD_IS_PCI) { + /* This was intended for enabeling the interrupt on the + * PCI card. However it seems that it's already enabled + * and as PCI interrupts can be shared, there is no real + * reason to have to turn it off. */ + } + (void) sx_in_off (bp, 0); /* Turn off interrupts. */ +} + +void turn_ints_on (struct specialix_board *bp) +{ + if (bp->flags & SX_BOARD_IS_PCI) { + /* play with the PCI chip. See comment above */ + } + (void) sx_in (bp, 0); /* Turn ON interrupts. */ +} + /* Called with disabled interrupts */ extern inline int sx_setup_board(struct specialix_board * bp) @@ -930,14 +965,12 @@ if (bp->flags & SX_BOARD_ACTIVE) return 0; - error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL); + error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp); if (error) return error; - IRQ_to_board[bp->irq] = bp; - (void) sx_in (bp, 0); /* Turn ON interrupts. */ - + turn_ints_on (bp); bp->flags |= SX_BOARD_ACTIVE; MOD_INC_USE_COUNT; @@ -953,11 +986,13 @@ bp->flags &= ~SX_BOARD_ACTIVE; - free_irq(bp->irq, NULL); - (void) sx_in_off (bp, 0); /* Turn off interrupts. */ +#if SPECIALIX_DEBUG > 2 + printk ("Freeing IRQ%d for board %d.\n", bp->irq, board_No (bp)); +#endif + free_irq(bp->irq, bp); + + turn_ints_off (bp); - IRQ_to_board[bp->irq] = NULL; - MOD_DEC_USE_COUNT; } @@ -1042,12 +1077,14 @@ /* Page 48 of version 2.0 of the CL-CD1865 databook */ if (tmp >= 12) { printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" - "Performance degradation is possible.\n", + "Performance degradation is possible.\n" + "Read specialix.txt for more info.\n", port_No (port), tmp); } else { printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" "Warning: overstressing Cirrus chip. " - "This might not work.\n", + "This might not work.\n" + "Read specialix.txt for more info.\n", port_No (port), tmp); } } @@ -2150,7 +2187,6 @@ return 1; } init_bh(SPECIALIX_BH, do_specialix_bh); - memset(IRQ_to_board, 0, sizeof(IRQ_to_board)); memset(&specialix_driver, 0, sizeof(specialix_driver)); specialix_driver.magic = TTY_DRIVER_MAGIC; specialix_driver.name = "ttyW"; @@ -2240,7 +2276,7 @@ void specialix_setup(char *str, int * ints) { int i; - + for (i=0;i /* CONFIG_VGA_CONSOLE */ #include #include #include diff -u --recursive --new-file v2.0.35/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.0.35/linux/drivers/char/tty_io.c Mon Jul 13 13:46:28 1998 +++ linux/drivers/char/tty_io.c Sun Nov 15 10:32:55 1998 @@ -2049,9 +2049,6 @@ #ifdef CONFIG_RISCOM8 riscom8_init(); #endif -#ifdef CONFIG_BAYCOM - baycom_init(); -#endif #ifdef CONFIG_SPECIALIX specialix_init(); #endif @@ -2059,4 +2056,3 @@ vcs_init(); return 0; } - diff -u --recursive --new-file v2.0.35/linux/drivers/char/vt.c linux/drivers/char/vt.c --- v2.0.35/linux/drivers/char/vt.c Sun Nov 15 10:49:34 1998 +++ linux/drivers/char/vt.c Sun Nov 15 10:32:56 1998 @@ -172,8 +172,10 @@ if (hz > 20 && hz < 32767) count = 1193180 / hz; + if (!count) + kd_nosound(0); /* ignore multiple simultaneous requests for sound */ - if (!set_bit(0, &mksound_lock)) { + else if (!set_bit(0, &mksound_lock)) { /* set_bit in 2.0.x is same as test-and-set in 2.1.x */ del_timer(&sound_timer); if (count) { @@ -189,9 +191,7 @@ sound_timer.expires = jiffies+ticks; add_timer(&sound_timer); } - } else - kd_nosound(0); - + } mksound_lock = 0; } return; diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in --- v2.0.35/linux/drivers/isdn/Config.in Mon Aug 4 17:33:59 1997 +++ linux/drivers/isdn/Config.in Sun Nov 15 10:32:56 1998 @@ -7,20 +7,43 @@ bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP fi + #bool 'Support dynamic timeout-rules' CONFIG_ISDN_TIMEOUT_RULES + #if [ "$CONFIG_ISDN_TIMEOUT_RULES" != "n" ]; then + # bool 'Use masqueraded addresses for rule-matching' CONFIG_ISDN_TIMRU_USE_MASQ + #fi + #bool 'Support budget-accounting' CONFIG_ISDN_BUDGET fi bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN +dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO + if [ "$CONFIG_HISAX_EURO" != "n" ]; then + bool 'Support for german chargeinfo' CONFIG_DE_AOC + bool 'Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE + bool 'Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC + fi bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6 bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0 bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3 + bool 'HiSax Support for Teles 16.3c' CONFIG_HISAX_TELES3C + bool 'HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI + bool 'HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1 - bool 'HiSax Support for Elsa ISA cards' CONFIG_HISAX_ELSA_PCC - bool 'HiSax Support for Elsa PCMCIA card' CONFIG_HISAX_ELSA_PCMCIA + bool 'HiSax Support for AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI + bool 'HiSax Support for AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA + bool 'HiSax Support for Elsa cards' CONFIG_HISAX_ELSA bool 'HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2 + bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA + bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM + bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT + bool 'HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER + bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER + bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC + bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET + bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY fi if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/b1capi.c linux/drivers/isdn/avmb1/b1capi.c --- v2.0.35/linux/drivers/isdn/avmb1/b1capi.c Thu Aug 21 15:14:34 1997 +++ linux/drivers/isdn/avmb1/b1capi.c Sun Nov 15 10:32:56 1998 @@ -1,11 +1,74 @@ /* - * $Id: b1capi.c,v 1.4.2.1 1997/07/12 08:18:59 calle Exp $ + * $Id: b1capi.c,v 1.4.2.19 1998/10/25 14:36:14 fritz Exp $ * * CAPI 2.0 Module for AVM B1-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1capi.c,v $ + * Revision 1.4.2.19 1998/10/25 14:36:14 fritz + * Backported from MIPS (Cobalt). + * + * Revision 1.4.2.18 1998/03/20 20:34:37 calle + * port valid check now only for T1, because of the PCI and PCMCIA cards. + * + * Revision 1.4.2.17 1998/03/20 14:38:17 calle + * capidrv: prepared state machines for suspend/resume/hold + * capidrv: fix bug in state machine if B1/T1 is out of nccis + * b1capi: changed some errno returns. + * b1capi: detect if you try to add same T1 to different io address. + * b1capi: change number of nccis depending on number of channels. + * b1lli: cosmetics + * + * Revision 1.4.2.16 1998/03/20 09:01:08 calle + * Changes capi_register handling to get full support for 30 bchannels. + * + * Revision 1.4.2.15 1998/03/18 17:43:26 calle + * T1 with fastlink, bugfix for multicontroller support in capidrv.c + * + * Revision 1.4.2.14 1998/03/04 17:33:47 calle + * Changes for T1. + * + * Revision 1.4.2.13 1998/02/27 15:40:41 calle + * T1 running with slow link. bugfix in capi_release. + * + * Revision 1.4.2.12 1998/02/24 17:58:25 calle + * changes for T1. + * + * Revision 1.4.2.11 1998/01/27 16:12:49 calle + * Support for PCMCIA B1/M1/M2 ready. + * + * Revision 1.4.2.10 1998/01/26 14:53:30 calle + * interface change for pcmcia cards + * + * Revision 1.4.2.9 1998/01/23 16:49:27 calle + * added functions for pcmcia cards, + * avmb1_addcard returns now the controller number. + * + * Revision 1.4.2.8 1998/01/16 14:04:15 calle + * Decoding of manufacturer part of capi_profile, now show linetype and + * protocol if possible. + * + * Revision 1.4.2.7 1998/01/15 15:33:34 calle + * print cardtype, d2 protocol and linetype after load. + * + * Revision 1.4.2.6 1997/12/08 06:58:41 calle + * correct typo. + * + * Revision 1.4.2.5 1997/12/07 19:59:54 calle + * more changes for M1/T1/B1 + config + * + * Revision 1.4.2.4 1997/11/26 16:57:20 calle + * more changes for B1/M1/T1. + * + * Revision 1.4.2.3 1997/11/26 10:46:52 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * + * Revision 1.4.2.2 1997/10/19 14:44:36 calle + * fixed capi_get_version. + * * Revision 1.4.2.1 1997/07/12 08:18:59 calle * Correct bug in CARD_NR macro, so now more than one card will work. * Allow card reset, even if card is in running state. @@ -52,20 +115,16 @@ #include "capicmd.h" #include "capiutil.h" -static char *revision = "$Revision: 1.4.2.1 $"; +static char *revision = "$Revision: 1.4.2.19 $"; /* ------------------------------------------------------------- */ -int portbase = 0x150; -int irq = 15; int showcapimsgs = 0; /* used in lli.c */ int loaddebug = 0; #ifdef HAS_NEW_SYMTAB MODULE_AUTHOR("Carsten Paeth "); -MODULE_PARM(portbase, "i"); -MODULE_PARM(irq, "2-15i"); -MODULE_PARM(showcapimsgs, "0-3i"); +MODULE_PARM(showcapimsgs, "0-5i"); MODULE_PARM(loaddebug, "0-1i"); #endif @@ -99,7 +158,7 @@ /* ------------------------------------------------------------- */ -static struct capi_version driver_version = {2, 0, 0, 9}; +static struct capi_version driver_version = {2, 0, 1, 1<<4}; static char driver_serial[CAPI_SERIAL_LEN] = "4711"; static char capi_manufakturer[64] = "AVM Berlin"; @@ -111,7 +170,7 @@ #define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) -#define VALID_CARD(c) ((c) > 0 && (c) <= ncards) +#define VALID_CARD(c) ((c) > 0 && (c) <= CAPI_MAXCONTR) #define CARD(c) (&cards[(c)-1]) #define CARDNR(cp) (((cp)-cards)+1) @@ -128,6 +187,17 @@ /* -------- util functions ------------------------------------ */ +static char *cardtype2str(int cardtype) +{ + switch (cardtype) { + default: + case AVM_CARDTYPE_B1: return "B1"; + case AVM_CARDTYPE_M1: return "M1"; + case AVM_CARDTYPE_M2: return "M2"; + case AVM_CARDTYPE_T1: return "T1"; + } +} + static inline int capi_cmd_valid(__u8 cmd) { switch (cmd) { @@ -271,7 +341,7 @@ } } APPL(appl)->releasing--; - if (APPL(appl)->releasing == 0) { + if (APPL(appl)->releasing <= 0) { APPL(appl)->signal = 0; APPL_MARK_FREE(appl); printk(KERN_INFO "b1capi: appl %d down\n", appl); @@ -404,25 +474,36 @@ /* -------- card ready callback ------------------------------- */ + void avmb1_card_ready(avmb1_card * card) { + struct capi_profile *profp = + (struct capi_profile *)card->version[VER_PROFILE]; + char *dversion = card->version[VER_DRIVER]; __u16 appl; + char *cardname, cname[20]; + __u32 flag; + int nbchan = profp->nbchannel; card->cversion.majorversion = 2; card->cversion.minorversion = 0; - card->cversion.majormanuversion = (card->version[VER_DRIVER][0] - '0') << 4; - card->cversion.majormanuversion |= (card->version[VER_DRIVER][2] - '0'); - card->cversion.minormanuversion = (card->version[VER_DRIVER][3] - '0') << 4; - card->cversion.minormanuversion |= (card->version[VER_DRIVER][5] - '0') * 10; - card->cversion.minormanuversion |= (card->version[VER_DRIVER][6] - '0'); + card->cversion.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); + card->cversion.majormanuversion |= ((dversion[2] - '0') & 0xf); + card->cversion.minormanuversion = (dversion[3] - '0') << 4; + card->cversion.minormanuversion |= + (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); card->cardstate = CARD_RUNNING; - for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { if (VALID_APPLID(appl) && !APPL(appl)->releasing) { + int nconn, want = APPL(appl)->rparam.level3cnt; + + if (want > 0) nconn = want; + else nconn = nbchan * -want; + if (nconn == 0) nconn = nbchan; + B1_send_register(card->port, appl, - 1024 * (APPL(appl)->rparam.level3cnt+1), - APPL(appl)->rparam.level3cnt, + 1024 * (nconn+1), nconn, APPL(appl)->rparam.datablkcnt, APPL(appl)->rparam.datablklen); } @@ -430,10 +511,45 @@ set_bit(CARDNR(card), ¬ify_up_set); queue_task(&tq_state_notify, &tq_scheduler); - printk(KERN_NOTICE "b1capi: card %d ready.\n", CARDNR(card)); + + flag = ((__u8 *)(profp->manu))[1]; + switch (flag) { + case 0: cardname = cardtype2str(card->cardtype); break; + case 3: cardname = "PCMCIA B"; break; + case 4: cardname = "PCMCIA M1"; break; + case 5: cardname = "PCMCIA M2"; break; + case 6: cardname = "B1 V3.0"; break; + case 7: cardname = "B1 PCI"; break; + default: cardname = cname; break; + sprintf(cname, "AVM?%u", (unsigned int)flag); + break; + } + printk(KERN_NOTICE "b1capi: card %d \"%s\" ready.\n", + CARDNR(card), cardname); + flag = ((__u8 *)(profp->manu))[3]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n", + CARDNR(card), + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + flag = ((__u8 *)(profp->manu))[5]; + if (flag) + printk(KERN_NOTICE "b1capi: card %d Linetype:%s%s%s%s\n", + CARDNR(card), + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); } -static void avmb1_card_down(avmb1_card * card) +static void avmb1_card_down(avmb1_card * card, int notify) { __u16 appl; @@ -460,52 +576,135 @@ /* ------------------------------------------------------------- */ -int avmb1_addcard(int port, int irq) + +int avmb1_registercard(int port, int irq, int cardtype, int allocio) { struct avmb1_card *card; - int irqval; + int irqval,i; - card = &cards[ncards]; + for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ; + + if (i == CAPI_MAXCONTR) { + printk(KERN_ERR "b1capi: out of controller slots\n"); + return -ENFILE; + } + + card = &cards[i]; memset(card, 0, sizeof(avmb1_card)); - sprintf(card->name, "avmb1-%d", ncards + 1); + sprintf(card->name, "avmb1-%d", CARDNR(card)); - request_region(port, AVMB1_PORTLEN, card->name); + if (allocio) + request_region(port, AVMB1_PORTLEN, card->name); - if ((irqval = request_irq(irq, avmb1_interrupt, SA_SHIRQ, card->name, card)) != 0) { + if ((irqval = request_irq(irq, avmb1_interrupt, + SA_SHIRQ, card->name, card)) != 0) { printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n", irq, irqval); - release_region((unsigned short) port, AVMB1_PORTLEN); - return -EIO; + release_region(port, AVMB1_PORTLEN); + return -EBUSY; } + + card->cardstate = CARD_DETECTED; ncards++; - card->cnr = ncards; + card->cnr = CARDNR(card); card->port = port; card->irq = irq; - card->cardstate = CARD_DETECTED; - return 0; + card->cardtype = cardtype; + return card->cnr; +} + +int avmb1_addcard(int port, int irq, int cardtype) +{ + return avmb1_registercard(port, irq, cardtype, 1); } -int avmb1_probecard(int port, int irq) +int avmb1_detectcard(int port, int irq, int cardtype) { int rc; - if (check_region((unsigned short) port, AVMB1_PORTLEN)) { - printk(KERN_WARNING - "b1capi: ports 0x%03x-0x%03x in use.\n", - portbase, portbase + AVMB1_PORTLEN); - return -EIO; - } - if (!B1_valid_irq(irq)) { - printk(KERN_WARNING "b1capi: irq %d not valid.\n", irq); - return -EIO; + if (!B1_valid_irq(irq, cardtype)) { + printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n", + irq, cardtype2str(cardtype)); + return -EINVAL; + } + if (!B1_valid_port(port, cardtype)) { + printk(KERN_WARNING "b1capi: port 0x%x not valid for %s-card.\n", + port, cardtype2str(cardtype)); + return -EINVAL; } - if ((rc = B1_detect(port)) != 0) { - printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc); + B1_reset(port); + if ((rc = B1_detect(port, cardtype)) != 0) { + printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n", + cardtype2str(cardtype), port, rc); return -EIO; } B1_reset(port); - printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port); + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port); + break; + case AVM_CARDTYPE_T1: + break; + } + + return 0; +} + +int avmb1_probecard(int port, int irq, int cardtype) +{ + if (check_region(port, AVMB1_PORTLEN)) { + printk(KERN_WARNING + "b1capi: ports 0x%03x-0x%03x in use.\n", + port, port + AVMB1_PORTLEN); + return -EBUSY; + } + return avmb1_detectcard(port, irq, cardtype); +} + +int avmb1_unregistercard(int cnr, int freeio) +{ + avmb1_card * card; + if (!VALID_CARD(cnr)) + return -ESRCH; + card = CARD(cnr); + + if (card->cardstate == CARD_FREE) + return -ESRCH; + if (card->cardstate == CARD_RUNNING) + avmb1_card_down(card, freeio); + + if (card->cardstate != CARD_FREE) + if (card->cardtype == AVM_CARDTYPE_T1) + T1_reset(card->port); + + free_irq(card->irq, card); + if (freeio) + release_region(card->port, AVMB1_PORTLEN); + card->cardstate = CARD_FREE; + return 0; +} + +int avmb1_resetcard(int cnr) +{ + avmb1_card * card; + + if (!VALID_CARD(cnr)) + return -ESRCH; + card = CARD(cnr); + if (card->cardstate == CARD_FREE) + return -ESRCH; + + if (card->cardstate == CARD_RUNNING) + avmb1_card_down(card, 0); + + B1_reset(card->port); + B1_reset(card->port); + + card->cardstate = CARD_DETECTED; return 0; } @@ -517,7 +716,7 @@ static int capi_installed(void) { int i; - for (i = 0; i < ncards; i++) { + for (i = 0; i < CAPI_MAXCONTR; i++) { if (cards[i].cardstate == CARD_RUNNING) return 1; } @@ -526,6 +725,7 @@ static __u16 capi_register(capi_register_params * rparam, __u16 * applidp) { + int nconn, want = rparam->level3cnt; int i; int appl; @@ -544,14 +744,21 @@ memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params)); - for (i = 0; i < ncards; i++) { + for (i = 0; i < CAPI_MAXCONTR; i++) { + struct capi_profile *profp = + (struct capi_profile *)cards[i].version[VER_PROFILE]; + if (cards[i].cardstate != CARD_RUNNING) continue; + + if (want > 0) nconn = want; + else nconn = profp->nbchannel * -want; + if (nconn == 0) nconn = profp->nbchannel; + B1_send_register(cards[i].port, appl, - 1024 * (APPL(appl)->rparam.level3cnt + 1), - APPL(appl)->rparam.level3cnt, - APPL(appl)->rparam.datablkcnt, - APPL(appl)->rparam.datablklen); + 1024 * (nconn+1), nconn, + APPL(appl)->rparam.datablkcnt, + APPL(appl)->rparam.datablklen); } *applidp = appl; printk(KERN_INFO "b1capi: appl %d up\n", appl); @@ -564,20 +771,18 @@ struct sk_buff *skb; int i; - if (ncards == 0) - return CAPI_REGNOTINSTALLED; if (!VALID_APPLID(applid) || APPL(applid)->releasing) return CAPI_ILLAPPNR; while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0) kfree_skb(skb, FREE_READ); - for (i = 0; i < ncards; i++) { + for (i = 0; i < CAPI_MAXCONTR; i++) { if (cards[i].cardstate != CARD_RUNNING) { continue; } APPL(applid)->releasing++; B1_send_release(cards[i].port, applid); } - if (APPL(applid)->releasing == 0) { + if (APPL(applid)->releasing <= 0) { APPL(applid)->signal = 0; APPL_MARK_FREE(applid); printk(KERN_INFO "b1capi: appl %d down\n", applid); @@ -661,7 +866,7 @@ if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) return 0x2002; - memcpy((void *) verp, CARD(contr)->version[VER_SERIAL], + memcpy((void *) verp, &CARD(contr)->cversion, sizeof(capi_version)); return CAPI_NOERROR; } @@ -698,33 +903,87 @@ static int capi_manufacturer(unsigned int cmd, void *data) { unsigned long flags; - avmb1_loaddef ldef; - avmb1_carddef cdef; + avmb1_loadandconfigdef ldef; + avmb1_extcarddef cdef; avmb1_resetdef rdef; + avmb1_getdef gdef; avmb1_card *card; int rc; switch (cmd) { case AVMB1_ADDCARD: - if ((rc = copy_from_user((void *) &cdef, data, - sizeof(avmb1_carddef)))) - return rc; - if (!B1_valid_irq(cdef.irq)) - return -EINVAL; + case AVMB1_ADDCARD_WITH_TYPE: + if (cmd == AVMB1_ADDCARD) { + if ((rc = copy_from_user((void *) &cdef, data, + sizeof(avmb1_carddef)))) + return rc; + cdef.cardtype = AVM_CARDTYPE_B1; + } else { + if ((rc = copy_from_user((void *) &cdef, data, + sizeof(avmb1_extcarddef)))) + return rc; + } - if ((rc = avmb1_probecard(cdef.port, cdef.irq)) != 0) + if ((rc = avmb1_probecard(cdef.port, cdef.irq, cdef.cardtype)) != 0) return rc; - return avmb1_addcard(cdef.port, cdef.irq); + if (cdef.cardtype == AVM_CARDTYPE_T1) { + int i; + for (i=0; i < CAPI_MAXCONTR; i++) { + if ( cards[i].cardstate != CARD_FREE + && cards[i].cardtype == AVM_CARDTYPE_T1 + && cards[i].cardnr == cdef.cardnr) { + printk(KERN_ERR + "b1capi: T1-HEMA-card-%d already at 0x%x\n", + cdef.cardnr, cards[i].port); + return -EBUSY; + } + } + rc = T1_detectandinit(cdef.port,cdef.irq,cdef.cardnr); + if (rc) { + printk(KERN_NOTICE "b1capi: NO T1-HEMA-card-%d at 0x%x (%d)\n", + cdef.cardnr, cdef.port, rc); + return -EIO; + } + printk(KERN_NOTICE "b1capi: T1-HEMA-card-%d at 0x%x\n", + cdef.cardnr, cdef.port); + } + + rc = avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype); + if (rc < 0) + return rc; + /* don't want to change interface t + addcard/probecard/registercard */ + if (cdef.cardtype == AVM_CARDTYPE_T1) { + int i; + for (i=0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cnr == rc) { + cards[i].cardnr = cdef.cardnr; + break; + } + } + } + return rc; case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: - if ((rc = copy_from_user((void *) &ldef, data, - sizeof(avmb1_loaddef)))) - return rc; - if (!VALID_CARD(ldef.contr) || ldef.t4file.len <= 0) { - if (loaddebug) - printk(KERN_DEBUG "b1capi: load: invalid parameter contr=%d len=%d\n", ldef.contr, ldef.t4file.len); + if (cmd == AVMB1_LOAD) { + if ((rc = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loaddef)))) + return rc; + ldef.t4config.len = 0; + ldef.t4config.data = 0; + } else { + if ((rc = copy_from_user((void *) &ldef, data, + sizeof(avmb1_loadandconfigdef)))) + return rc; + } + if (!VALID_CARD(ldef.contr)) + return -ESRCH; + + if (ldef.t4file.len <= 0) { + printk(KERN_DEBUG "b1capi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len); return -EINVAL; } @@ -746,13 +1005,34 @@ } B1_reset(card->port); + + if (loaddebug) { + printk(KERN_DEBUG "b1capi: loading contr %d\n", + ldef.contr); + } + if ((rc = B1_load_t4file(card->port, &ldef.t4file))) { B1_reset(card->port); printk(KERN_ERR "b1capi: failed to load t4file!!\n"); card->cardstate = CARD_DETECTED; return rc; } + B1_disable_irq(card->port); + + if (ldef.t4config.len > 0) { /* load config */ + if (loaddebug) { + printk(KERN_DEBUG "b1capi: loading config to contr %d\n", + ldef.contr); + } + if ((rc = B1_load_config(card->port, &ldef.t4config))) { + B1_reset(card->port); + printk(KERN_ERR "b1capi: failed to load config!!\n"); + card->cardstate = CARD_DETECTED; + return rc; + } + } + if (loaddebug) { printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n", ldef.contr); @@ -770,8 +1050,7 @@ card->cardstate = CARD_INITSTATE; save_flags(flags); cli(); - B1_assign_irq(card->port, card->irq); - B1_enable_irq(card->port); + B1_setinterrupt(card->port, card->irq, card->cardtype); restore_flags(flags); if (loaddebug) { @@ -782,7 +1061,14 @@ /* * init card */ - B1_send_init(card->port, AVM_NAPPS, AVM_NNCCI, card->cnr - 1); + if (card->cardtype == AVM_CARDTYPE_T1) + B1_send_init(card->port, AVM_NAPPS, + AVM_NNCCI_PER_CHANNEL*30, + card->cnr - 1); + else + B1_send_init(card->port, AVM_NAPPS, + AVM_NNCCI_PER_CHANNEL*2, + card->cnr - 1); if (loaddebug) { printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n", @@ -799,25 +1085,45 @@ return -EINTR; } return 0; + case AVMB1_RESETCARD: if ((rc = copy_from_user((void *) &rdef, data, sizeof(avmb1_resetdef)))) return rc; - if (!VALID_CARD(rdef.contr)) - return -EINVAL; + return avmb1_resetcard(rdef.contr); - card = CARD(rdef.contr); + case AVMB1_GET_CARDINFO: + if ((rc = copy_from_user((void *) &gdef, data, + sizeof(avmb1_getdef)))) + return rc; - if (card->cardstate == CARD_RUNNING) - avmb1_card_down(card); + if (!VALID_CARD(gdef.contr)) + return -ESRCH; - card->cardstate = CARD_DETECTED; + card = CARD(gdef.contr); - B1_reset(card->port); - B1_reset(card->port); + gdef.cardstate = card->cardstate; + gdef.cardtype = card->cardtype; + + if ((rc = copy_to_user(data, (void *) &gdef, + sizeof(avmb1_getdef)))) + return rc; return 0; + case AVMB1_REMOVECARD: + if ((rc = copy_from_user((void *) &rdef, data, + sizeof(avmb1_resetdef)))) + return rc; + if (!VALID_CARD(rdef.contr)) + return -ESRCH; + + card = CARD(rdef.contr); + + if (card->cardstate != CARD_DETECTED) + return -EBUSY; + + return avmb1_unregistercard(rdef.contr, 1); } return -EINVAL; } @@ -855,6 +1161,7 @@ userp->next = capi_users; capi_users = userp; MOD_INC_USE_COUNT; + printk(KERN_NOTICE "b1capi: %s attached\n", userp->name); return &avmb1_interface; } @@ -868,6 +1175,7 @@ *pp = userp->next; userp->next = 0; MOD_DEC_USE_COUNT; + printk(KERN_NOTICE "b1capi: %s detached\n", userp->name); return 0; } } @@ -884,6 +1192,10 @@ EXPORT_SYMBOL(detach_capi_interface); EXPORT_SYMBOL(avmb1_addcard); EXPORT_SYMBOL(avmb1_probecard); +EXPORT_SYMBOL(avmb1_registercard); +EXPORT_SYMBOL(avmb1_unregistercard); +EXPORT_SYMBOL(avmb1_resetcard); +EXPORT_SYMBOL(avmb1_detectcard); #else static struct symbol_table capidev_syms = { @@ -892,6 +1204,10 @@ X(detach_capi_interface), X(avmb1_addcard), X(avmb1_probecard), + X(avmb1_registercard), + X(avmb1_unregistercard), + X(avmb1_resetcard), + X(avmb1_detectcard), #include }; #endif @@ -910,7 +1226,6 @@ char *p; char rev[10]; - #ifndef HAS_NEW_SYMTAB /* No symbols to export, hide all symbols */ register_symtab(&capidev_syms); @@ -933,15 +1248,7 @@ strcpy(rev, " ??? "); #ifdef MODULE - if (portbase) { - int rc; - if ((rc = avmb1_probecard(portbase, irq)) != 0) - return rc; - if ((rc = avmb1_addcard(portbase, irq)) != 0) - return rc; - } else { - printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev); - } + printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev); #else printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: started\n", rev); #endif @@ -963,20 +1270,20 @@ strcpy(rev, " ??? "); } - for (i = 0; i < ncards; i++) { - /* - * disable card - */ - B1_disable_irq(cards[i].port); - B1_reset(cards[i].port); - B1_reset(cards[i].port); - /* - * free kernel resources - */ - free_irq(cards[i].irq, &cards[i]); - release_region(cards[i].port, AVMB1_PORTLEN); - + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (cards[i].cardstate != CARD_FREE) { + /* + * disable card + */ + B1_disable_irq(cards[i].port); + avmb1_resetcard(i+1); + /* + * free kernel resources + */ + avmb1_unregistercard(i+1, 1); + } } + schedule(); /* execute queued tasks .... */ printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev); } #endif diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/b1lli.c linux/drivers/isdn/avmb1/b1lli.c --- v2.0.35/linux/drivers/isdn/avmb1/b1lli.c Thu Aug 21 15:14:34 1997 +++ linux/drivers/isdn/avmb1/b1lli.c Sun Nov 15 10:32:57 1998 @@ -1,11 +1,48 @@ /* - * $Id: b1lli.c,v 1.1.2.1 1997/07/13 12:16:46 calle Exp $ + * $Id: b1lli.c,v 1.1.2.11 1998/10/25 14:36:18 fritz Exp $ * * ISDN lowlevel-module for AVM B1-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1lli.c,v $ + * Revision 1.1.2.11 1998/10/25 14:36:18 fritz + * Backported from MIPS (Cobalt). + * + * Revision 1.1.2.10 1998/03/20 20:34:41 calle + * port valid check now only for T1, because of the PCI and PCMCIA cards. + * + * Revision 1.1.2.9 1998/03/20 14:38:20 calle + * capidrv: prepared state machines for suspend/resume/hold + * capidrv: fix bug in state machine if B1/T1 is out of nccis + * b1capi: changed some errno returns. + * b1capi: detect if you try to add same T1 to different io address. + * b1capi: change number of nccis depending on number of channels. + * b1lli: cosmetics + * + * Revision 1.1.2.8 1998/03/18 17:43:29 calle + * T1 with fastlink, bugfix for multicontroller support in capidrv.c + * + * Revision 1.1.2.7 1998/03/04 17:33:50 calle + * Changes for T1. + * + * Revision 1.1.2.6 1998/02/27 15:40:44 calle + * T1 running with slow link. bugfix in capi_release. + * + * Revision 1.1.2.5 1998/02/13 16:28:28 calle + * first step for T1 + * + * Revision 1.1.2.4 1998/01/27 16:12:51 calle + * Support for PCMCIA B1/M1/M2 ready. + * + * Revision 1.1.2.3 1998/01/15 15:33:37 calle + * print cardtype, d2 protocol and linetype after load. + * + * Revision 1.1.2.2 1997/11/26 10:46:55 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * * Revision 1.1.2.1 1997/07/13 12:16:46 calle * bug fix for more than one controller in connect_req. * @@ -20,6 +57,7 @@ * * */ +/* #define FASTLINK_DEBUG */ #include #include @@ -34,6 +72,8 @@ #include "capicmd.h" #include "capiutil.h" +extern int showcapimsgs; + /* * LLI Messages to the ISDN-ControllerISDN Controller */ @@ -69,6 +109,11 @@ * B3Length data .... */ +#define SEND_CONFIG 0x21 /* + */ + +#define SEND_POLLACK 0x73 /* T1 Watchdog */ + /* * LLI Messages from the ISDN-ControllerISDN Controller */ @@ -112,6 +157,13 @@ #define RECEIVE_RELEASE 0x26 /* * int32 AppllID int32 0xffffffff */ +#define RECEIVE_TASK_READY 0x31 /* + * int32 tasknr + * int32 Length Taskname ... + */ + +#define WRITE_REGISTER 0x00 +#define READ_REGISTER 0x01 /* * port offsets @@ -124,9 +176,47 @@ #define B1_RESET 0x10 #define B1_ANALYSE 0x04 +/* Hema card T1 */ + +#define T1_FASTLINK 0x00 +#define T1_SLOWLINK 0x08 + +#define T1_READ B1_READ +#define T1_WRITE B1_WRITE +#define T1_INSTAT B1_INSTAT +#define T1_OUTSTAT B1_OUTSTAT +#define T1_IRQENABLE 0x05 +#define T1_FIFOSTAT 0x06 +#define T1_RESETLINK 0x10 +#define T1_ANALYSE 0x11 +#define T1_IRQMASTER 0x12 +#define T1_IDENT 0x17 +#define T1_RESETBOARD 0x1f + +#define T1F_IREADY 0x01 +#define T1F_IHALF 0x02 +#define T1F_IFULL 0x04 +#define T1F_IEMPTY 0x08 +#define T1F_IFLAGS 0xF0 + +#define T1F_OREADY 0x10 +#define T1F_OHALF 0x20 +#define T1F_OEMPTY 0x40 +#define T1F_OFULL 0x80 +#define T1F_OFLAGS 0xF0 + +/* there are HEMA cards with 1k and 4k FIFO out */ +#define FIFO_OUTBSIZE 256 +#define FIFO_INPBSIZE 512 +#define HEMA_VERSION_ID 0 +#define HEMA_PAL_ID 0 -static inline unsigned char b1outp(unsigned short base, +#define B1_STAT0(cardtype) ((cardtype) == AVM_CARDTYPE_M1 ? 0x81200000l : 0x80A00000l) +#define B1_STAT1(cardtype) (0x80E00000l) + + +static inline unsigned char b1outp(unsigned int base, unsigned short offset, unsigned char value) { @@ -134,6 +224,191 @@ return inb(base + B1_ANALYSE); } +static inline void t1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); +} + +static inline unsigned char t1inp(unsigned int base, + unsigned short offset) +{ + return inb(base + offset); +} + +static inline int B1_isfastlink(unsigned int base) +{ + return (inb(base + T1_IDENT) & ~0x82) == 1; +} +static inline unsigned char B1_fifostatus(unsigned int base) +{ + return inb(base + T1_FIFOSTAT); +} + +static inline int B1_rx_full(unsigned int base) +{ + return inb(base + B1_INSTAT) & 0x1; +} + +static inline unsigned char B1_get_byte(unsigned int base) +{ + unsigned long i = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + while (!B1_rx_full(base) && i > jiffies); + if (B1_rx_full(base)) + return inb(base + B1_READ); + printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base); + return 0; +} + +static inline unsigned int B1_get_word(unsigned int base) +{ + unsigned int val = 0; + val |= B1_get_byte(base); + val |= (B1_get_byte(base) << 8); + val |= (B1_get_byte(base) << 16); + val |= (B1_get_byte(base) << 24); + return val; +} + +static inline int B1_tx_empty(unsigned int base) +{ + return inb(base + B1_OUTSTAT) & 0x1; +} + +static inline void B1_put_byte(unsigned int base, unsigned char val) +{ + while (!B1_tx_empty(base)); + b1outp(base, B1_WRITE, val); +} + +static inline void B1_put_word(unsigned int base, unsigned int val) +{ + B1_put_byte(base, val & 0xff); + B1_put_byte(base, (val >> 8) & 0xff); + B1_put_byte(base, (val >> 16) & 0xff); + B1_put_byte(base, (val >> 24) & 0xff); +} + +static inline unsigned int B1_get_slice(unsigned int base, + unsigned char *dp) +{ + unsigned int len, i; +#ifdef FASTLINK_DEBUG + unsigned wcnt = 0, bcnt = 0; +#endif + + len = i = B1_get_word(base); + if (B1_isfastlink(base)) { + int status; + while (i > 0) { + status = B1_fifostatus(base) & (T1F_IREADY|T1F_IHALF); + if (i >= FIFO_INPBSIZE) status |= T1F_IFULL; + + switch (status) { + case T1F_IREADY|T1F_IHALF|T1F_IFULL: + insb(base+B1_READ, dp, FIFO_INPBSIZE); + dp += FIFO_INPBSIZE; + i -= FIFO_INPBSIZE; +#ifdef FASTLINK_DEBUG + wcnt += FIFO_INPBSIZE; +#endif + break; + case T1F_IREADY|T1F_IHALF: + insb(base+B1_READ,dp, i); +#ifdef FASTLINK_DEBUG + wcnt += i; +#endif + dp += i; + i = 0; + if (i == 0) + break; + /* fall through */ + default: + *dp++ = B1_get_byte(base); + i--; +#ifdef FASTLINK_DEBUG + bcnt++; +#endif + break; + } + } +#ifdef FASTLINK_DEBUG + if (wcnt) + printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n", + base, len, wcnt, bcnt); +#endif + } else { + while (i-- > 0) + *dp++ = B1_get_byte(base); + } + return len; +} + +static inline void B1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + B1_put_word(base, i); + if (B1_isfastlink(base)) { + int status; + while (i > 0) { + status = B1_fifostatus(base) & (T1F_OREADY|T1F_OHALF); + if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY; + switch (status) { + case T1F_OREADY|T1F_OHALF|T1F_OEMPTY: + outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE); + dp += FIFO_OUTBSIZE; + i -= FIFO_OUTBSIZE; + break; + case T1F_OREADY|T1F_OHALF: + outsb(base+B1_WRITE, dp, i); + dp += i; + i = 0; + break; + default: + B1_put_byte(base, *dp++); + i--; + break; + } + } + } else { + while (i-- > 0) + B1_put_byte(base, *dp++); + } +} + +static void b1_wr_reg(unsigned int base, + unsigned int reg, + unsigned int value) +{ + B1_put_byte(base, WRITE_REGISTER); + B1_put_word(base, reg); + B1_put_word(base, value); +} + +static inline unsigned int b1_rd_reg(unsigned int base, + unsigned int reg) +{ + B1_put_byte(base, READ_REGISTER); + B1_put_word(base, reg); + return B1_get_word(base); + +} + +static inline void b1_set_test_bit(unsigned int base, + int cardtype, + int onoff) +{ + b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20); +} + +static inline int b1_get_test_bit(unsigned int base, + int cardtype) +{ + return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0; +} + static int irq_table[16] = {0, 0, @@ -153,27 +428,92 @@ 112, /* irq 15 */ }; -int B1_valid_irq(unsigned irq) +static int hema_irq_table[16] = +{0, + 0, + 0, + 0x80, /* irq 3 */ + 0, + 0x90, /* irq 5 */ + 0, + 0xA0, /* irq 7 */ + 0, + 0xB0, /* irq 9 */ + 0xC0, /* irq 10 */ + 0xD0, /* irq 11 */ + 0xE0, /* irq 12 */ + 0, + 0, + 0xF0, /* irq 15 */ +}; + + +int B1_valid_irq(unsigned irq, int cardtype) { - return irq_table[irq] != 0; + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + return irq_table[irq & 0xf] != 0; + case AVM_CARDTYPE_T1: + return hema_irq_table[irq & 0xf] != 0; + } } -unsigned char B1_assign_irq(unsigned short base, unsigned irq) +int B1_valid_port(unsigned port, int cardtype) { - return b1outp(base, B1_RESET, irq_table[irq]); + switch (cardtype) { + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: +#if 0 /* problem with PCMCIA and PCI cards */ + switch (port) { + case 0x150: + case 0x250: + case 0x300: + case 0x340: + return 1; + } + return 0; +#else + return 1; +#endif + case AVM_CARDTYPE_T1: + return ((port & 0x7) == 0) && ((port & 0x30) != 0x30); + } +} + +void B1_setinterrupt(unsigned int base, + unsigned irq, int cardtype) +{ + switch (cardtype) { + case AVM_CARDTYPE_T1: + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_INSTAT, 0x02); + t1outp(base, T1_IRQMASTER, 0x08); + default: + case AVM_CARDTYPE_M1: + case AVM_CARDTYPE_M2: + case AVM_CARDTYPE_B1: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, irq_table[irq]); + b1outp(base, B1_INSTAT, 0x02); + } } -unsigned char B1_enable_irq(unsigned short base) +unsigned char B1_disable_irq(unsigned int base) { - return b1outp(base, B1_INSTAT, 0x02); + return b1outp(base, B1_INSTAT, 0x00); } -unsigned char B1_disable_irq(unsigned short base) +void T1_disable_irq(unsigned int base) { - return b1outp(base, B1_INSTAT, 0x00); + t1outp(base, T1_IRQMASTER, 0x00); } -void B1_reset(unsigned short base) +void B1_reset(unsigned int base) { b1outp(base, B1_RESET, 0); udelay(55 * 2 * 1000); /* 2 TIC's */ @@ -185,24 +525,39 @@ udelay(55 * 2 * 1000); /* 2 TIC's */ } -int B1_detect(unsigned short base) +void T1_reset(unsigned int base) +{ + /* reset T1 Controller */ + B1_reset(base); + /* disable irq on HEMA */ + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_OUTSTAT, 0x00); + t1outp(base, T1_IRQMASTER, 0x00); + /* reset HEMA board configuration */ + t1outp(base, T1_RESETBOARD, 0xf); +} + +int B1_detect(unsigned int base, int cardtype) { + int onoff, i; + + if (cardtype == AVM_CARDTYPE_T1) + return 0; + /* * Statusregister 0000 00xx */ if ((inb(base + B1_INSTAT) & 0xfc) || (inb(base + B1_OUTSTAT) & 0xfc)) return 1; - /* * Statusregister 0000 001x */ b1outp(base, B1_INSTAT, 0x2); /* enable irq */ - b1outp(base, B1_OUTSTAT, 0x2); + /* b1outp(base, B1_OUTSTAT, 0x2); */ if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 - || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2) + /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */) return 2; - /* * Statusregister 0000 000x */ @@ -211,76 +566,96 @@ if ((inb(base + B1_INSTAT) & 0xfe) || (inb(base + B1_OUTSTAT) & 0xfe)) return 3; + + for (onoff = !0, i= 0; i < 10 ; i++) { + b1_set_test_bit(base, cardtype, onoff); + if (b1_get_test_bit(base, cardtype) != onoff) + return 4; + onoff = !onoff; + } - return 0; -} + if (cardtype == AVM_CARDTYPE_M1) + return 0; -static inline int B1_rx_full(unsigned short base) -{ - return inb(base + B1_INSTAT) & 0x1; -} + if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01) + return 5; -static inline unsigned char B1_get_byte(unsigned short base) -{ - unsigned long i = jiffies + 5 * HZ; /* maximum wait time 5 sec */ - while (!B1_rx_full(base) && i > jiffies); - if (B1_rx_full(base)) - return inb(base + B1_READ); - printk(KERN_CRIT "b1lli: rx not full after 5 second\n"); return 0; } -static inline unsigned int B1_get_word(unsigned short base) -{ - unsigned int val = 0; - val |= B1_get_byte(base); - val |= (B1_get_byte(base) << 8); - val |= (B1_get_byte(base) << 16); - val |= (B1_get_byte(base) << 24); - return val; -} - -static inline int B1_tx_empty(unsigned short base) -{ - return inb(base + B1_OUTSTAT) & 0x1; -} - -static inline void B1_put_byte(unsigned short base, unsigned char val) +int T1_detectandinit(unsigned int base, unsigned irq, int cardnr) { - while (!B1_tx_empty(base)); - b1outp(base, B1_WRITE, val); -} + unsigned char cregs[8]; + unsigned char reverse_cardnr; + unsigned long flags; + unsigned char dummy; + int i; -static inline void B1_put_word(unsigned short base, unsigned int val) -{ - B1_put_byte(base, val & 0xff); - B1_put_byte(base, (val >> 8) & 0xff); - B1_put_byte(base, (val >> 16) & 0xff); - B1_put_byte(base, (val >> 24) & 0xff); -} + reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1) + | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3); + cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf); + cregs[1] = 0x00; /* fast & slow link connected to CON1 */ + cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */ + cregs[3] = 0; + cregs[4] = 0x11; /* zero wait state */ + cregs[5] = hema_irq_table[irq & 0xf]; + cregs[6] = 0; + cregs[7] = 0; -static inline unsigned int B1_get_slice(unsigned short base, - unsigned char *dp) -{ - unsigned int len, i; + save_flags(flags); + cli(); + /* board reset */ + t1outp(base, T1_RESETBOARD, 0xf); + udelay(100 * 1000); + dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */ + + /* write config */ + dummy = (base >> 4) & 0xff; + for (i=1;i<=0xf;i++) t1outp(base, i, dummy); + t1outp(base, HEMA_PAL_ID & 0xf, dummy); + t1outp(base, HEMA_PAL_ID >> 4, cregs[0]); + for(i=1;i<7;i++) t1outp(base, 0, cregs[i]); + t1outp(base, ((base >> 4)) & 0x3, cregs[7]); + restore_flags(flags); - len = i = B1_get_word(base); - while (i-- > 0) - *dp++ = B1_get_byte(base); - return len; -} + udelay(100 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); + udelay(10 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 1); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1); + udelay(100 * 1000); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); + udelay(10 * 1000); + t1outp(base, T1_FASTLINK+T1_ANALYSE, 0); + udelay(5 * 1000); + t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0); -static inline void B1_put_slice(unsigned short base, - unsigned char *dp, unsigned int len) -{ - B1_put_word(base, len); - while (len-- > 0) - B1_put_byte(base, *dp++); + if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */ + return 1; + if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */ + return 2; + if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0) + return 3; + if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70) + return 4; + if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0) + return 5; + if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1) + return 6; + if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */ + return 7; + if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0) + return 8; + if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0) + return 9; + return 0; } extern int loaddebug; -int B1_load_t4file(unsigned short base, avmb1_t4file * t4file) +int B1_load_t4file(unsigned int base, avmb1_t4file * t4file) { /* * Data is in user space !!! @@ -319,7 +694,63 @@ return 0; } -int B1_loaded(unsigned short base) +int B1_load_config(unsigned int base, avmb1_t4file * config) +{ + /* + * Data is in user space !!! + */ + unsigned char buf[256]; + unsigned char *dp; + int i, j, left, retval; + + + dp = config->data; + left = config->len; + if (left) { + B1_put_byte(base, SEND_CONFIG); + B1_put_word(base, 1); + B1_put_byte(base, SEND_CONFIG); + B1_put_word(base, left); + } + while (left > sizeof(buf)) { + retval = copy_from_user(buf, dp, sizeof(buf)); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", sizeof(buf)); + for (i = 0; i < sizeof(buf); ) { + B1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + B1_put_byte(base, buf[i++]); + } + } + if (loaddebug) + printk("ok\n"); + left -= sizeof(buf); + dp += sizeof(buf); + } + if (left) { + retval = copy_from_user(buf, dp, left); + if (retval) + return -EFAULT; + if (loaddebug) + printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", left); + for (i = 0; i < left; ) { + B1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + if (i < left) + B1_put_byte(base, buf[i++]); + else + B1_put_byte(base, 0); + } + } + if (loaddebug) + printk("ok\n"); + } + return 0; +} + +int B1_loaded(unsigned int base) { int i; unsigned char ans; @@ -331,7 +762,7 @@ break; } if (!B1_tx_empty(base)) { - printk(KERN_ERR "b1lli: B1_loaded: timeout tx\n"); + printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout tx\n", base); return 0; } B1_put_byte(base, SEND_POLL); @@ -343,11 +774,12 @@ printk(KERN_DEBUG "b1capi: loaded: ok\n"); return 1; } - printk(KERN_ERR "b1lli: B1_loaded: got 0x%x ???\n", ans); + printk(KERN_ERR "b1lli(0x%x): B1_loaded: got 0x%x ???\n", + base, ans); return 0; } } - printk(KERN_ERR "b1lli: B1_loaded: timeout rx\n"); + printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout rx\n", base); return 0; } @@ -368,7 +800,7 @@ * ------------------------------------------------------------------- */ -void B1_send_init(unsigned short port, +void B1_send_init(unsigned int port, unsigned int napps, unsigned int nncci, unsigned int cardnr) { unsigned long flags; @@ -382,7 +814,7 @@ restore_flags(flags); } -void B1_send_register(unsigned short port, +void B1_send_register(unsigned int port, __u16 appid, __u32 nmsg, __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize) { @@ -399,7 +831,7 @@ restore_flags(flags); } -void B1_send_release(unsigned short port, +void B1_send_release(unsigned int port, __u16 appid) { unsigned long flags; @@ -411,9 +843,7 @@ restore_flags(flags); } -extern int showcapimsgs; - -void B1_send_message(unsigned short port, struct sk_buff *skb) +void B1_send_message(unsigned int port, struct sk_buff *skb) { unsigned long flags; __u16 len = CAPIMSG_LEN(skb->data); @@ -479,6 +909,7 @@ unsigned NCCI; unsigned WindowSize; +t1retry: if (!B1_rx_full(card->port)) return; @@ -555,7 +986,7 @@ WindowSize = B1_get_word(card->port); if (showcapimsgs) - printk(KERN_DEBUG "b1lli: NEW_NCCI app %u ncci 0x%x\n", ApplId, NCCI); + printk(KERN_DEBUG "b1lli(0x%x): NEW_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI); avmb1_handle_new_ncci(card, ApplId, NCCI, WindowSize); @@ -567,19 +998,23 @@ NCCI = B1_get_word(card->port); if (showcapimsgs) - printk(KERN_DEBUG "b1lli: FREE_NCCI app %u ncci 0x%x\n", ApplId, NCCI); + printk(KERN_DEBUG "b1lli(0x%x): FREE_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI); avmb1_handle_free_ncci(card, ApplId, NCCI); break; case RECEIVE_START: + if (card->cardtype == AVM_CARDTYPE_T1) { + B1_put_byte(card->port, SEND_POLLACK); + /* printk(KERN_DEBUG "b1lli: T1 watchdog\n"); */ + } if (card->blocked) - printk(KERN_DEBUG "b1lli: RESTART\n"); + printk(KERN_DEBUG "b1lli(0x%x): RESTART\n", card->port); card->blocked = 0; break; case RECEIVE_STOP: - printk(KERN_DEBUG "b1lli: STOP\n"); + printk(KERN_DEBUG "b1lli(0x%x): STOP\n", card->port); card->blocked = 1; break; @@ -588,14 +1023,24 @@ card->versionlen = B1_get_slice(card->port, card->versionbuf); card->cardstate = CARD_ACTIVE; parse_version(card); - printk(KERN_INFO "b1lli: %s-card (%s) with %s now active\n", + printk(KERN_INFO "b1lli(0x%x): %s-card (%s) now active\n", + card->port, card->version[VER_CARDTYPE], - card->version[VER_DRIVER], - card->version[VER_PROTO]); + card->version[VER_DRIVER]); avmb1_card_ready(card); break; + case RECEIVE_TASK_READY: + ApplId = (unsigned) B1_get_word(card->port); + MsgLen = B1_get_slice(card->port, card->msgbuf); + card->msgbuf[MsgLen] = 0; + printk(KERN_INFO "b1lli(0x%x): Task %d \"%s\" ready.\n", + card->port, ApplId, card->msgbuf); + break; default: - printk(KERN_ERR "b1lli: B1_handle_interrupt: 0x%x ???\n", b1cmd); + printk(KERN_ERR "b1lli(0x%x): B1_handle_interrupt: 0x%x ???\n", + card->port, b1cmd); break; } + if (card->cardtype == AVM_CARDTYPE_T1) + goto t1retry; } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c --- v2.0.35/linux/drivers/isdn/avmb1/b1pci.c Mon Aug 4 17:33:59 1997 +++ linux/drivers/isdn/avmb1/b1pci.c Sun Nov 15 10:32:57 1998 @@ -1,11 +1,20 @@ /* - * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 calle Exp $ + * $Id: b1pci.c,v 1.2.2.2 1998/01/23 16:49:30 calle Exp $ * * Module for AVM B1 PCI-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1pci.c,v $ + * Revision 1.2.2.2 1998/01/23 16:49:30 calle + * added functions for pcmcia cards, + * avmb1_addcard returns now the controller number. + * + * Revision 1.2.2.1 1997/11/26 10:46:57 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * * Revision 1.2 1997/05/18 09:24:13 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -34,7 +43,7 @@ #define PCI_DEVICE_ID_AVM_B1 0x700 #endif -static char *revision = "$Revision: 1.2 $"; +static char *revision = "$Revision: 1.2.2.2 $"; /* ------------------------------------------------------------- */ @@ -98,13 +107,13 @@ printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", ioaddr, irq); - if ((rc = avmb1_probecard(ioaddr, irq)) != 0) { + if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1)) != 0) { printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n", ioaddr, irq); return rc; } - if ((rc = avmb1_addcard(ioaddr, irq)) != 0) + if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1)) < 0) return rc; } return 0; diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c --- v2.0.35/linux/drivers/isdn/avmb1/capidrv.c Thu Aug 21 15:14:34 1997 +++ linux/drivers/isdn/avmb1/capidrv.c Sun Nov 15 10:32:57 1998 @@ -1,11 +1,49 @@ /* - * $Id: capidrv.c,v 1.3.2.1 1997/07/13 12:16:48 calle Exp $ + * $Id: capidrv.c,v 1.3.2.12 1998/09/11 15:37:11 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.c,v $ + * Revision 1.3.2.12 1998/09/11 15:37:11 calle + * Started with support for CAPI channel allocation/bundling. + * + * Revision 1.3.2.11 1998/04/02 10:27:59 calle + * version check for D2 trace was wrong :-( + * + * Revision 1.3.2.10 1998/03/20 14:38:24 calle + * capidrv: prepared state machines for suspend/resume/hold + * capidrv: fix bug in state machine if B1/T1 is out of nccis + * b1capi: changed some errno returns. + * b1capi: detect if you try to add same T1 to different io address. + * b1capi: change number of nccis depending on number of channels. + * b1lli: cosmetics + * + * Revision 1.3.2.9 1998/03/20 09:01:12 calle + * Changes capi_register handling to get full support for 30 bchannels. + * + * Revision 1.3.2.8 1998/03/18 17:51:28 calle + * added controller number to error messages + * + * Revision 1.3.2.7 1998/02/27 15:40:47 calle + * T1 running with slow link. bugfix in capi_release. + * + * Revision 1.3.2.6 1998/02/02 19:51:13 calle + * Fixed vbox (audio) acceptb. + * + * Revision 1.3.2.5 1997/10/29 09:35:29 calle + * correct byteorder problem with new isdnlog interface. + * + * Revision 1.3.2.4 1997/10/26 15:04:24 calle + * prepared isdnlog interface for d2-trace in newer firmware. + * + * Revision 1.3.2.3 1997/10/24 06:37:00 calle + * changed LISTEN cipmask, now we can distinguish voice, fax und data calls. + * + * Revision 1.3.2.2 1997/10/08 05:42:25 calle + * Added isdnlog support. patch to isdnlog needed. + * * Revision 1.3.2.1 1997/07/13 12:16:48 calle * bug fix for more than one controller in connect_req. * @@ -45,13 +83,14 @@ #include #include #include +#include #include "compat.h" #include "capiutil.h" #include "capicmd.h" #include "capidrv.h" -static char *revision = "$Revision: 1.3.2.1 $"; +static char *revision = "$Revision: 1.3.2.12 $"; int debugmode = 0; #ifdef HAS_NEW_SYMTAB @@ -109,6 +148,7 @@ __u16 msgid; /* to identfy CONNECT_CONF */ int chan; int state; + int leasedline; struct capidrv_ncci { struct capidrv_ncci *next; struct capidrv_plci *plcip; @@ -125,8 +165,15 @@ } *bchans; struct capidrv_plci *plci_list; + + /* for q931 data */ + __u8 q931_buf[4096]; + __u8 *q931_read; + __u8 *q931_write; + __u8 *q931_end; }; + struct capidrv_data { __u16 appid; int ncontr; @@ -144,6 +191,9 @@ static capidrv_data global; static struct capi_interface *capifuncs; +static void handle_dtrace_data(capidrv_contr *card, + int send, int level2, __u8 *data, __u16 len); + /* -------- convert functions ---------------------------------------- */ static inline __u32 b1prot(int l2, int l3) @@ -170,9 +220,8 @@ default: return 0; case ISDN_PROTO_L2_HDLC: - return 1; case ISDN_PROTO_L2_TRANS: - return 0; + return 1; } } @@ -338,8 +387,8 @@ return; } } - printk(KERN_ERR "capidrv: free_plci %p (0x%x) not found, Huh?\n", - plcip, plcip->plci); + printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n", + card->contrnr, plcip, plcip->plci); } /* -------- ncci management ------------------------------------------ */ @@ -437,15 +486,15 @@ static struct listenstatechange listentable[] = { - {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ}, - {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ}, - {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR}, - {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR}, - {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, - {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, - {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, - {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, - {}, + {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ}, + {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ}, + {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR}, + {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR}, + {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, + {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, + {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, + {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, + {}, }; static void listen_change_state(capidrv_contr * card, int event) @@ -454,15 +503,15 @@ while (p->event) { if (card->state == p->actstate && p->event == event) { if (debugmode) - printk(KERN_DEBUG "capidrv: listen_change_state %d -> %d\n", - card->state, p->nextstate); + printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n", + card->contrnr, card->state, p->nextstate); card->state = p->nextstate; return; } p++; } - printk(KERN_ERR "capidrv: listen_change_state state=%d event=%d ????\n", - card->state, event); + printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n", + card->contrnr, card->state, event); } @@ -492,46 +541,57 @@ static struct plcistatechange plcitable[] = { /* P-0 */ - {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0}, - {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0}, - {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0}, + {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0}, + {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0}, + {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0}, + {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, 0}, /* P-0.1 */ - {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0}, - {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0}, - {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0}, + {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0}, + {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, /* P-1 */ - {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, - {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, -{ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-ACT */ - {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, 0}, + {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, 0}, /* P-2 */ - {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, - {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0}, - {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0}, - {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, + {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0}, + {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, 0}, /* P-3 */ -{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, -{ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0}, -{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, - {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-4 */ - {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, - {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, -{ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, - {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0}, + {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-5 */ -{ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, + {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0}, /* P-6 */ - {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, - {}, + {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, + /* P-0.Res */ + {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0}, + {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, 0}, + /* P-RES */ + {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, 0}, + /* P-HELD */ + {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, 0}, + {}, }; static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event) @@ -540,8 +600,8 @@ while (p->event) { if (plci->state == p->actstate && p->event == event) { if (debugmode) - printk(KERN_DEBUG "capidrv: plci_change_state:0x%x %d -> %d\n", - plci->plci, plci->state, p->nextstate); + printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n", + card->contrnr, plci->plci, plci->state, p->nextstate); plci->state = p->nextstate; if (p->changefunc) p->changefunc(card, plci); @@ -549,8 +609,8 @@ } p++; } - printk(KERN_ERR "capidrv: plci_change_state:0x%x state=%d event=%d ????\n", - plci->plci, plci->state, event); + printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n", + card->contrnr, plci->plci, plci->state, event); } /* ------------------------------------------------------------------ */ @@ -567,7 +627,7 @@ ncci->plcip->plci, 0, /* BChannelinformation */ 0, /* Keypadfacility */ - 0, /* Useruserdata */ + 0, /* Useruserdata */ /* $$$$ */ 0 /* Facilitydataarray */ ); send_message(card, &cmsg); @@ -592,34 +652,35 @@ static struct nccistatechange nccitable[] = { /* N-0 */ - {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0}, - {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0}, + {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0}, + {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0}, /* N-0.1 */ - {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0}, - {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, 0}, + {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0}, + {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0}, /* N-1 */ - {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0}, - {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0}, + {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0}, + {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0}, {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, - {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-2 */ - {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0}, - {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, -{ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0}, + {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-ACT */ - {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0}, - {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, - {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-3 */ - {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, + {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0}, {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, - {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, + {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0}, /* N-4 */ - {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, - {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR, 0}, + {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0}, + {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,0}, /* N-5 */ - {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0}, - {}, + {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0}, + {}, }; static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event) @@ -628,8 +689,8 @@ while (p->event) { if (ncci->state == p->actstate && p->event == event) { if (debugmode) - printk(KERN_DEBUG "capidrv: ncci_change_state:0x%x %d -> %d\n", - ncci->ncci, ncci->state, p->nextstate); + printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n", + card->contrnr, ncci->ncci, ncci->state, p->nextstate); if (p->nextstate == ST_NCCI_PREVIOUS) { ncci->state = ncci->oldstate; ncci->oldstate = p->actstate; @@ -643,8 +704,8 @@ } p++; } - printk(KERN_ERR "capidrv: ncci_change_state:0x%x state=%d event=%d ????\n", - ncci->ncci, ncci->state, event); + printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n", + card->contrnr, ncci->ncci, ncci->state, event); } /* ------------------------------------------------------------------- */ @@ -677,8 +738,8 @@ case CAPI_LISTEN_CONF: /* Controller */ if (debugmode) - printk(KERN_DEBUG "capidrv: listenconf Info=0x%4x (%s) cipmask=0x%x\n", - cmsg->Info, capi_info2str(cmsg->Info), card->cipmask); + printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n", + card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask); if (cmsg->Info) { listen_change_state(card, EV_LISTEN_CONF_ERROR); } else if (card->cipmask == 0) { @@ -689,8 +750,55 @@ break; case CAPI_MANUFACTURER_IND: /* Controller */ + if ( cmsg->ManuID == 0x214D5641 + && cmsg->Class == 0 + && cmsg->Function == 1) { + __u8 *data = cmsg->ManuData+3; + __u16 len = cmsg->ManuData[0]; + __u16 layer; + int direction; + if (len == 255) { + len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8)); + data += 2; + } + len -= 2; + layer = ((*(data-1)) << 8) | *(data-2); + if (layer & 0x300) + direction = (layer & 0x200) ? 0 : 1; + else direction = (layer & 0x800) ? 0 : 1; + if (layer & 0x0C00) { + if ((layer & 0xff) == 0x80) { + handle_dtrace_data(card, direction, 1, data, len); + break; + } + } else if ((layer & 0xff) < 0x80) { + handle_dtrace_data(card, direction, 0, data, len); + break; + } + printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n", + card->contrnr, + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController, layer); + break; + } goto ignored; case CAPI_MANUFACTURER_CONF: /* Controller */ + if (cmsg->ManuID == 0x214D5641) { + char *s = 0; + switch (cmsg->Class) { + case 0: break; + case 1: s = "unknown class"; break; + case 2: s = "unknown function"; break; + default: s = "unkown error"; break; + } + if (s) + printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n", + card->contrnr, + capi_cmd2str(cmsg->Command, cmsg->Subcommand), + cmsg->adr.adrController, + cmsg->Function, s); + break; + } goto ignored; case CAPI_FACILITY_IND: /* Controller/plci/ncci */ goto ignored; @@ -702,14 +810,16 @@ goto ignored; default: - printk(KERN_ERR "capidrv: got %s from controller 0x%x ???", + printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController); } return; ignored: - printk(KERN_INFO "capidrv: %s from controller 0x%x ignored\n", + printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrController); } @@ -722,12 +832,12 @@ int chan; if ((chan = new_bchan(card)) == -1) { - printk(KERN_ERR "capidrv: incoming call on not existing bchan ?\n"); + printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr); return; } bchan = &card->bchans[chan]; if ((plcip = new_plci(card, chan)) == 0) { - printk(KERN_ERR "capidrv: incoming call: no memory, sorry.\n"); + printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr); return; } bchan->incoming = 1; @@ -749,7 +859,8 @@ cmd.parm.setup.plan = cmsg->CallingPartyNumber[1]; cmd.parm.setup.screen = cmsg->CallingPartyNumber[2]; - printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s\n", + printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n", + card->contrnr, cmd.parm.setup.phone, cmd.parm.setup.si1, cmd.parm.setup.si2, @@ -766,7 +877,8 @@ cmsg->Reject = 1; /* ignore */ send_message(card, cmsg); plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); - printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s ignored\n", + printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n", + card->contrnr, cmd.parm.setup.phone, cmd.parm.setup.si1, cmd.parm.setup.si2, @@ -783,7 +895,8 @@ * and CONNECT_RESP already sent. */ if (plcip->state == ST_PLCI_INCOMING) { - printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s tty alerting\n", + printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n", + card->contrnr, cmd.parm.setup.phone, cmd.parm.setup.si1, cmd.parm.setup.si2, @@ -800,7 +913,8 @@ plcip->msgid = cmsg->Messagenumber; send_message(card, cmsg); } else { - printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s on netdev\n", + printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n", + card->contrnr, cmd.parm.setup.phone, cmd.parm.setup.si1, cmd.parm.setup.si2, @@ -843,7 +957,8 @@ case CAPI_DISCONNECT_IND: /* plci */ if (cmsg->Reason) { - printk(KERN_INFO "capidrv: %s reason 0x%x (%s) for plci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI); } @@ -861,7 +976,8 @@ case CAPI_DISCONNECT_CONF: /* plci */ if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); @@ -874,7 +990,8 @@ case CAPI_ALERT_CONF: /* plci */ if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); @@ -887,7 +1004,8 @@ case CAPI_CONNECT_CONF: /* plci */ if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrPLCI); @@ -920,7 +1038,7 @@ nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI); if (!nccip) { - printk(KERN_ERR "capidrv: no mem for ncci, sorry\n"); + printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr); break; /* $$$$ */ } capi_fill_CONNECT_B3_REQ(cmsg, @@ -960,7 +1078,8 @@ break; } } - printk(KERN_ERR "capidrv: %s\n", capi_cmsg2str(cmsg)); + printk(KERN_ERR "capidrv-%d: %s\n", + card->contrnr, capi_cmsg2str(cmsg)); break; case CAPI_CONNECT_ACTIVE_CONF: /* plci */ @@ -976,18 +1095,21 @@ goto ignored; default: - printk(KERN_ERR "capidrv: got %s for plci 0x%x ???", + printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); } return; ignored: - printk(KERN_INFO "capidrv: %s for plci 0x%x ignored\n", + printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); return; notfound: - printk(KERN_ERR "capidrv: %s: plci 0x%x not found\n", + printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrPLCI); return; @@ -1021,8 +1143,8 @@ cmd.arg = nccip->chan; card->interface.statcallb(&cmd); - printk(KERN_INFO "capidrv: chan %d up with ncci 0x%x\n", - nccip->chan, nccip->ncci); + printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n", + card->contrnr, nccip->chan, nccip->ncci); break; case CAPI_CONNECT_B3_ACTIVE_CONF: /* ncci */ @@ -1046,9 +1168,10 @@ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP); break; } - printk(KERN_ERR "capidrv: no mem for ncci, sorry\n"); + printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr); } else { - printk(KERN_ERR "capidrv: %s: plci for ncci 0x%x not found\n", + printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } @@ -1071,7 +1194,8 @@ nccip->ncci = cmsg->adr.adrNCCI; if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrNCCI); @@ -1118,7 +1242,8 @@ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) goto notfound; if (cmsg->Info) { - printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n", + printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->Info, capi_info2str(cmsg->Info), cmsg->adr.adrNCCI); @@ -1127,6 +1252,9 @@ break; case CAPI_RESET_B3_IND: /* ncci */ + if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) + goto notfound; + ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND); capi_cmsg_answer(cmsg); send_message(card, cmsg); break; @@ -1140,18 +1268,21 @@ goto ignored; default: - printk(KERN_ERR "capidrv: got %s for ncci 0x%x ???", + printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } return; ignored: - printk(KERN_INFO "capidrv: %s for ncci 0x%x ignored\n", + printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); return; notfound: - printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n", + printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); } @@ -1169,7 +1300,8 @@ return; } if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { - printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n", + printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n", + card->contrnr, capi_cmd2str(cmsg->Command, cmsg->Subcommand), cmsg->adr.adrNCCI); kfree_skb(skb, FREE_READ); @@ -1190,7 +1322,8 @@ while ((*capifuncs->capi_get_message) (global.appid, &skb) == CAPI_NOERROR) { capi_message2cmsg(&s_cmsg, skb->data); if (debugmode > 1) - printk(KERN_DEBUG "capidrv_signal: %s\n", capi_cmsg2str(&s_cmsg)); + printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n", + applid, capi_cmsg2str(&s_cmsg)); if (s_cmsg.Command == CAPI_DATA_B3 && s_cmsg.Subcommand == CAPI_IND) { @@ -1209,23 +1342,171 @@ /* ------------------------------------------------------------------- */ +#define PUTBYTE_TO_STATUS(card, byte) \ + do { \ + *(card)->q931_write++ = (byte); \ + if ((card)->q931_write > (card)->q931_end) \ + (card)->q931_write = (card)->q931_buf; \ + } while (0) + +static void handle_dtrace_data(capidrv_contr *card, + int send, int level2, __u8 *data, __u16 len) +{ + long flags; + __u8 *p, *end; + isdn_ctrl cmd; + + if (!len) { + printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n", + card->contrnr, len); + return; + } + + save_flags(flags); + cli(); + + if (level2) { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '2'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } else { + PUTBYTE_TO_STATUS(card, 'D'); + PUTBYTE_TO_STATUS(card, '3'); + PUTBYTE_TO_STATUS(card, send ? '>' : '<'); + PUTBYTE_TO_STATUS(card, ':'); + } + + for (p = data, end = data+len; p < end; p++) { + __u8 w; + PUTBYTE_TO_STATUS(card, ' '); + w = (*p >> 4) & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + w = *p & 0xf; + PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); + } + PUTBYTE_TO_STATUS(card, '\n'); + + restore_flags(flags); + + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = len*3+5; + card->interface.statcallb(&cmd); +} + +/* ------------------------------------------------------------------- */ + static _cmsg cmdcmsg; static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) { switch (c->arg) { default: - printk(KERN_DEBUG "capidrv: capidrv_ioctl(%ld) called ??\n", c->arg); + printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n", + card->contrnr, c->arg); return -EINVAL; } return -EINVAL; } +/* + * Handle leased lines (CAPI-Bundling) + */ + +struct internal_bchannelinfo { + unsigned short channelalloc; + unsigned short operation; + unsigned char cmask[31]; +}; + +static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep) +{ + unsigned long bmask = 0; + int active = !0; + char *s; + int i; + + if (strncmp(teln, "FV:", 3) != 0) + return 1; + s = teln + 3; + while (*s && *s == ' ') s++; + if (!*s) return -2; + if (*s == 'p' || *s == 'P') { + active = 0; + s++; + } + if (*s == 'a' || *s == 'A') { + active = !0; + s++; + } + while (*s) { + int digit1 = 0; + int digit2 = 0; + if (!isdigit(*s)) return -3; + while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; } + if (digit1 <= 0 && digit1 > 30) return -4; + if (*s == 0 || *s == ',' || *s == ' ') { + bmask |= (1 << digit1); + digit1 = 0; + if (*s) s++; + continue; + } + if (*s != '-') return -5; + s++; + if (!isdigit(*s)) return -3; + while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; } + if (digit2 <= 0 && digit2 > 30) return -4; + if (*s == 0 || *s == ',' || *s == ' ') { + if (digit1 > digit2) + for (i = digit2; i <= digit1 ; i++) + bmask |= (1 << i); + else + for (i = digit1; i <= digit2 ; i++) + bmask |= (1 << i); + digit1 = digit2 = 0; + if (*s) s++; + continue; + } + return -6; + } + if (activep) *activep = active; + if (bmaskp) *bmaskp = bmask; + return 0; +} + +static int FVteln2capi20(char *teln, __u8 AdditionalInfo[1+2+2+31]) +{ + unsigned long bmask; + int active; + int rc, i; + + rc = decodeFVteln(teln, &bmask, &active); + if (rc) return rc; + /* Length */ + AdditionalInfo[0] = 2+2+31; + /* Channel: 3 => use channel allocation */ + AdditionalInfo[1] = 3; AdditionalInfo[2] = 0; + /* Operation: 0 => DTE mode, 1 => DCE mode */ + if (active) { + AdditionalInfo[3] = 0; AdditionalInfo[4] = 0; + } else { + AdditionalInfo[3] = 1; AdditionalInfo[4] = 0; + } + /* Channel mask array */ + AdditionalInfo[5] = 0; /* no D-Channel */ + for (i=1; i <= 30; i++) + AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0; + return 0; +} + static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) { isdn_ctrl cmd; struct capidrv_bchan *bchan; struct capidrv_plci *plcip; + __u8 AdditionalInfo[1+2+2+31]; + int rc, isleasedline = 0; if (c->command == ISDN_CMD_IOCTL) return capidrv_ioctl(c, card); @@ -1236,7 +1517,8 @@ __u8 called[ISDN_MSNLEN + 2]; if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n", + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n", + card->contrnr, c->arg, c->parm.setup.phone, c->parm.setup.si1, @@ -1246,7 +1528,8 @@ bchan = &card->bchans[c->arg % card->nbchan]; if (bchan->plcip) { - printk(KERN_ERR "capidrv: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n", + printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n", + card->contrnr, c->arg, c->parm.setup.phone, c->parm.setup.si1, @@ -1261,14 +1544,26 @@ strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num)); strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum)); - calling[0] = strlen(bchan->mynum) + 2; - calling[1] = 0; - calling[2] = 0x80; - strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN); - - called[0] = strlen(bchan->num) + 1; - called[1] = 0x80; - strncpy(called + 2, bchan->num, ISDN_MSNLEN); + rc = FVteln2capi20(bchan->num, AdditionalInfo); + isleasedline = (rc == 0); + if (rc < 0) + printk(KERN_ERR "capidrv-%d: WARNING: illegal leased linedefinition \"%s\"\n", card->contrnr, bchan->num); + + if (isleasedline) { + calling[0] = 0; + called[0] = 0; + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr); + } else { + calling[0] = strlen(bchan->mynum) + 2; + calling[1] = 0; + calling[2] = 0x80; + strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN); + called[0] = strlen(bchan->num) + 1; + called[1] = 0x80; + strncpy(called + 2, bchan->num, ISDN_MSNLEN); + } + capi_fill_CONNECT_REQ(&cmdcmsg, global.appid, @@ -1288,7 +1583,8 @@ 0, /* BC */ 0, /* LLC */ 0, /* HLC */ - 0, /* BChannelinformation */ + /* BChannelinformation */ + isleasedline ? AdditionalInfo : 0, 0, /* Keypadfacility */ 0, /* Useruserdata */ 0 /* Facilitydataarray */ @@ -1301,6 +1597,7 @@ return -1; } plcip->msgid = cmdcmsg.Messagenumber; + plcip->leasedline = isleasedline; plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ); send_message(card, &cmdcmsg); return 0; @@ -1308,10 +1605,11 @@ case ISDN_CMD_ACCEPTD: - if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTD(ch=%ld)\n", - c->arg); bchan = &card->bchans[c->arg % card->nbchan]; + if (debugmode) + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n", + card->contrnr, + c->arg, bchan->l2, bchan->l3); capi_fill_CONNECT_RESP(&cmdcmsg, global.appid, @@ -1339,19 +1637,22 @@ case ISDN_CMD_ACCEPTB: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTB(ch=%ld)\n", + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n", + card->contrnr, c->arg); return -ENOSYS; case ISDN_CMD_HANGUP: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_HANGUP(ch=%ld)\n", + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n", + card->contrnr, c->arg); bchan = &card->bchans[c->arg % card->nbchan]; if (bchan->disconnecting) { if (debugmode) - printk(KERN_DEBUG "capidrv: chan %ld already disconnecting ...\n", + printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n", + card->contrnr, c->arg); return 0; } @@ -1390,23 +1691,26 @@ case ISDN_CMD_SETL2: if (debugmode) - printk(KERN_DEBUG "capidrv: set L2 on chan %ld to %ld\n", + printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n", + card->contrnr, (c->arg & 0xff), (c->arg >> 8)); - bchan = &card->bchans[c->arg % card->nbchan]; + bchan = &card->bchans[(c->arg & 0xff) % card->nbchan]; bchan->l2 = (c->arg >> 8); return 0; case ISDN_CMD_SETL3: if (debugmode) - printk(KERN_DEBUG "capidrv: set L3 on chan %ld to %ld\n", + printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n", + card->contrnr, (c->arg & 0xff), (c->arg >> 8)); - bchan = &card->bchans[c->arg % card->nbchan]; + bchan = &card->bchans[(c->arg & 0xff) % card->nbchan]; bchan->l3 = (c->arg >> 8); return 0; case ISDN_CMD_SETEAZ: if (debugmode) - printk(KERN_DEBUG "capidrv: set EAZ \"%s\" on chan %ld\n", + printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n", + card->contrnr, c->parm.num, c->arg); bchan = &card->bchans[c->arg % card->nbchan]; strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN); @@ -1414,46 +1718,54 @@ case ISDN_CMD_CLREAZ: if (debugmode) - printk(KERN_DEBUG "capidrv: clearing EAZ on chan %ld\n", c->arg); + printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n", + card->contrnr, c->arg); bchan = &card->bchans[c->arg % card->nbchan]; bchan->msn[0] = 0; return 0; case ISDN_CMD_LOCK: if (debugmode > 1) - printk(KERN_DEBUG "capidrv: ISDN_CMD_LOCK (%ld)\n", c->arg); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_LOCK (%ld)\n", card->contrnr, c->arg); MOD_INC_USE_COUNT; break; case ISDN_CMD_UNLOCK: if (debugmode > 1) - printk(KERN_DEBUG "capidrv: ISDN_CMD_UNLOCK (%ld)\n", c->arg); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_UNLOCK (%ld)\n", + card->contrnr, c->arg); MOD_DEC_USE_COUNT; break; /* never called */ case ISDN_CMD_GETL2: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL2\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL2\n", + card->contrnr); return -ENODEV; case ISDN_CMD_GETL3: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL3\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL3\n", + card->contrnr); return -ENODEV; case ISDN_CMD_GETEAZ: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_GETEAZ\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETEAZ\n", + card->contrnr); return -ENODEV; case ISDN_CMD_SETSIL: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_SETSIL\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_SETSIL\n", + card->contrnr); return -ENODEV; case ISDN_CMD_GETSIL: if (debugmode) - printk(KERN_DEBUG "capidrv: ISDN_CMD_GETSIL\n"); + printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETSIL\n", + card->contrnr); return -ENODEV; default: - printk(KERN_ERR "capidrv: ISDN_CMD_%d, Huh?\n", c->command); + printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n", + card->contrnr, c->command); return -EINVAL; } return 0; @@ -1467,8 +1779,8 @@ return capidrv_command(c, card); printk(KERN_ERR - "capidrv: if_command %d called with invalid driverId %d!\n", - c->command, c->driver); + "capidrv-%d: if_command %d called with invalid driverId %d!\n", + card->contrnr, c->command, c->driver); return -ENODEV; } @@ -1484,15 +1796,15 @@ __u16 errcode; if (!card) { - printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n", - id); + printk(KERN_ERR "capidrv-%d: if_sendbuf called with invalid driverId %d!\n", + card->contrnr, id); return 0; } bchan = &card->bchans[channel % card->nbchan]; nccip = bchan->nccip; if (!nccip || nccip->state != ST_NCCI_ACTIVE) { - printk(KERN_ERR "capidrv: if_sendbuf: %s:%d: chan not up!\n", - card->name, channel); + printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n", + card->contrnr, card->name, channel); return 0; } capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++, @@ -1507,12 +1819,13 @@ if (skb_headroom(skb) < msglen) { struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len); if (!nskb) { - printk(KERN_ERR "capidrv: if_sendbuf: no memory\n"); + printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n", + card->contrnr); return 0; } #if 0 - printk(KERN_DEBUG "capidrv: only %d bytes headroom\n", - skb_headroom(skb)); + printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom\n", + card->contrnr, skb_headroom(skb)); #endif SET_SKB_FREE(nskb); memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen); @@ -1542,6 +1855,82 @@ } } +static int if_readstat(__u8 *buf, int len, int user, int id, int channel) +{ + capidrv_contr *card = findcontrbydriverid(id); + int count; + __u8 *p; + + if (!card) { + printk(KERN_ERR "capidrv-%d: if_readstat called with invalid driverId %d!\n", + card->contrnr, id); + return -ENODEV; + } + + for (p=buf, count=0; count < len; p++, count++) { + if (user) + put_user(*card->q931_read++, p); + else + *p = *card->q931_read++; + if (card->q931_read > card->q931_end) + card->q931_read = card->q931_buf; + } + return count; + +} + +static void enable_dchannel_trace(capidrv_contr *card) +{ + __u8 manufacturer[CAPI_MANUFACTURER_LEN]; + capi_version version; + __u16 contr = card->contrnr; + __u16 errcode; + __u16 avmversion[3]; + + errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n", + card->name, errcode); + return; + } + if (strstr(manufacturer, "AVM") == 0) { + printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n", + card->name); + return; + } + errcode = (*capifuncs->capi_get_version)(contr, &version); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "%s: can't get version (0x%x)\n", + card->name, errcode); + return; + } + avmversion[0] = (version.majormanuversion >> 4) & 0x0f; + avmversion[1] = (version.majormanuversion << 4) & 0xf0; + avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; + avmversion[2] |= version.minormanuversion & 0x0f; + + 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++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\200\014\000\000"); + } else { + printk(KERN_INFO "%s: D3 trace enabled\n", card->name); + capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid, + card->msgid++, + contr, + 0x214D5641, /* ManuID */ + 0, /* Class */ + 1, /* Function */ + (_cstruct)"\004\002\003\000\000"); + } + send_message(card, &cmdcmsg); +} + static int capidrv_addcontr(__u16 contr, struct capi_profile *profp) { capidrv_contr *card; @@ -1571,7 +1960,7 @@ card->interface.command = if_command; card->interface.writebuf_skb = if_sendbuf; card->interface.writecmd = 0; - card->interface.readstat = 0; + card->interface.readstat = if_readstat; card->interface.features = ISDN_FEATURE_L2_X75I | ISDN_FEATURE_L2_X75UI | ISDN_FEATURE_L2_X75BUI | @@ -1584,6 +1973,9 @@ card->next = global.contr_list; global.contr_list = card; global.ncontr++; + card->q931_read = card->q931_buf; + card->q931_write = card->q931_buf; + card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1; if (!register_isdn(&card->interface)) { global.contr_list = global.contr_list->next; @@ -1603,7 +1995,7 @@ cmd.command = ISDN_STAT_RUN; card->interface.statcallb(&cmd); - card->cipmask = 1; /* any */ + card->cipmask = 0x1FFF03FF; /* any */ card->cipmask2 = 0; capi_fill_LISTEN_REQ(&cmdcmsg, global.appid, @@ -1619,6 +2011,9 @@ printk(KERN_INFO "%s: now up (%d B channels)\n", card->name, card->nbchan); + if (card->nbchan == 2) /* no T1 */ + enable_dchannel_trace(card); + return 0; } @@ -1709,8 +2104,8 @@ } else strcpy(rev, " ??? "); - rparam.level3cnt = 2; - rparam.datablkcnt = 8; + rparam.level3cnt = -2; /* number of bchannels twice */ + rparam.datablkcnt = 16; rparam.datablklen = 2048; errcode = (*capifuncs->capi_register) (&rparam, &global.appid); if (errcode) { diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/capidrv.h linux/drivers/isdn/avmb1/capidrv.h --- v2.0.35/linux/drivers/isdn/avmb1/capidrv.h Mon Aug 4 17:33:59 1997 +++ linux/drivers/isdn/avmb1/capidrv.h Sun Nov 15 10:32:57 1998 @@ -1,11 +1,19 @@ /* - * $Id: capidrv.h,v 1.1 1997/03/04 21:50:33 calle Exp $ + * $Id: capidrv.h,v 1.1.2.1 1998/03/20 14:38:28 calle Exp $ * * ISDN4Linux Driver, using capi20 interface (kernelcapi) * * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capidrv.h,v $ + * Revision 1.1.2.1 1998/03/20 14:38:28 calle + * capidrv: prepared state machines for suspend/resume/hold + * capidrv: fix bug in state machine if B1/T1 is out of nccis + * b1capi: changed some errno returns. + * b1capi: detect if you try to add same T1 to different io address. + * b1capi: change number of nccis depending on number of channels. + * b1lli: cosmetics + * * Revision 1.1 1997/03/04 21:50:33 calle * Frirst version in isdn4linux * @@ -49,34 +57,70 @@ #define ST_PLCI_ACCEPTING 6 /* P-4 */ #define ST_PLCI_DISCONNECTING 7 /* P-5 */ #define ST_PLCI_DISCONNECTED 8 /* P-6 */ +#define ST_PLCI_RESUMEING 9 /* P-0.Res */ +#define ST_PLCI_RESUME 10 /* P-Res */ +#define ST_PLCI_HELD 11 /* P-HELD */ -#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1 */ -#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0 */ -#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1 */ -#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1 */ -#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2 */ -#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT */ +#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1 + */ +#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0 + */ +#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1 + */ +#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1 + */ +#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2 + */ +#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT + */ #define EV_PLCI_CONNECT_REJECT 7 /* P-2 -> P-5 - P-3 -> P-5 */ + P-3 -> P-5 + */ #define EV_PLCI_DISCONNECT_REQ 8 /* P-1 -> P-5 P-2 -> P-5 P-3 -> P-5 P-4 -> P-5 - P-ACT -> P-5 */ + P-ACT -> P-5 + P-Res -> P-5 (*) + P-HELD -> P-5 (*) + */ #define EV_PLCI_DISCONNECT_IND 9 /* P-1 -> P-6 P-2 -> P-6 P-3 -> P-6 P-4 -> P-6 P-5 -> P-6 - P-ACT -> P-6 */ + P-ACT -> P-6 + P-Res -> P-6 (*) + P-HELD -> P-6 (*) + */ #define EV_PLCI_FACILITY_IND_DOWN 10 /* P-0.1 -> P-5 P-1 -> P-5 P-ACT -> P-5 P-2 -> P-5 P-3 -> P-5 - P-4 -> P-5 */ -#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0 */ -#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0 */ + P-4 -> P-5 + */ +#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0 + */ +#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0 + */ + +#define EV_PLCI_RESUME_REQ 13 /* P-0 -> P-0.Res + */ +#define EV_PLCI_RESUME_CONF_OK 14 /* P-0.Res -> P-Res + */ +#define EV_PLCI_RESUME_CONF_ERROR 15 /* P-0.Res -> P-0 + */ +#define EV_PLCI_RESUME_IND 16 /* P-Res -> P-ACT + */ +#define EV_PLCI_HOLD_IND 17 /* P-ACT -> P-HELD + */ +#define EV_PLCI_RETRIEVE_IND 18 /* P-HELD -> P-ACT + */ +#define EV_PLCI_SUSPEND_IND 19 /* P-ACT -> P-5 + */ +#define EV_PLCI_CD_IND 20 /* P-2 -> P-5 + */ /* * per ncci state machine diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/capiutil.c linux/drivers/isdn/avmb1/capiutil.c --- v2.0.35/linux/drivers/isdn/avmb1/capiutil.c Mon Jul 13 13:46:28 1998 +++ linux/drivers/isdn/avmb1/capiutil.c Sun Nov 15 10:32:57 1998 @@ -1,5 +1,5 @@ /* - * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $ + * $Id: capiutil.c,v 1.3.2.1 1998/08/03 15:52:21 paul Exp $ * * CAPI 2.0 convert capi message to capi message struct * @@ -7,6 +7,10 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.c,v $ + * Revision 1.3.2.1 1998/08/03 15:52:21 paul + * various changes from 2.0.3[45] kernel sources, as suggested by + * Oliver.Lauer@coburg.baynet.de + * * Revision 1.3 1997/05/18 09:24:18 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/compat.h linux/drivers/isdn/avmb1/compat.h --- v2.0.35/linux/drivers/isdn/avmb1/compat.h Wed Oct 15 15:25:22 1997 +++ linux/drivers/isdn/avmb1/compat.h Sun Nov 15 10:32:57 1998 @@ -1,11 +1,14 @@ /* - * $Id: compat.h,v 1.1 1997/03/04 21:50:36 calle Exp $ + * $Id: compat.h,v 1.1.2.1 1998/10/25 14:36:22 fritz Exp $ * * Headerfile for Compartibility between different kernel versions * * (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: compat.h,v $ + * Revision 1.1.2.1 1998/10/25 14:36:22 fritz + * Backported from MIPS (Cobalt). + * * Revision 1.1 1997/03/04 21:50:36 calle * Frirst version in isdn4linux * @@ -21,6 +24,7 @@ #define __COMPAT_H__ #include +#include #include #if LINUX_VERSION_CODE >= 0x020112 /* 2.1.18 */ diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile --- v2.0.35/linux/drivers/isdn/hisax/Makefile Mon Aug 4 17:33:59 1997 +++ linux/drivers/isdn/hisax/Makefile Sun Nov 15 10:32:57 1998 @@ -1,7 +1,14 @@ L_OBJS := M_OBJS := -O_OBJS := isdnl1.o config.o tei.o isdnl2.o isdnl3.o \ - q931.o callc.o fsm.o +LX_OBJS := +MX_OBJS := +O_OBJS := +OX_OBJS := +L_TARGET := +O_TARGET := + +O_OBJS := isdnl1.o tei.o isdnl2.o isdnl3.o \ + lmgr.o q931.o callc.o fsm.o cert.o # EXTRA_CFLAGS += -S @@ -17,31 +24,127 @@ O_OBJS += l3_1tr6.o endif +ISAC_OBJ := +ARCOFI_OBJ := +HSCX_OBJ := +ISAR_OBJ := +HFC_OBJ := +HFC_2BDS0 := + ifeq ($(CONFIG_HISAX_16_0),y) O_OBJS += teles0.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif ifeq ($(CONFIG_HISAX_16_3),y) O_OBJS += teles3.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_TELESPCI),y) + O_OBJS += telespci.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_S0BOX),y) + O_OBJS += s0box.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif ifeq ($(CONFIG_HISAX_AVM_A1),y) O_OBJS += avm_a1.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o endif -ifeq ($(CONFIG_HISAX_ELSA_PCC),y) - O_OBJS += elsa.o +ifeq ($(CONFIG_HISAX_AVM_A1_PCMCIA),y) + O_OBJS += avm_a1p.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_FRITZPCI),y) + O_OBJS += avm_pci.o + ISAC_OBJ := isac.o endif -ifeq ($(CONFIG_HISAX_ELSA_PCMCIA),y) + +ifeq ($(CONFIG_HISAX_ELSA),y) O_OBJS += elsa.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o + ARCOFI_OBJ := arcofi.o endif ifeq ($(CONFIG_HISAX_IX1MICROR2),y) O_OBJS += ix1_micro.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_DIEHLDIVA),y) + O_OBJS += diva.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_ASUSCOM),y) + O_OBJS += asuscom.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_TELEINT),y) + O_OBJS += teleint.o + ISAC_OBJ := isac.o + HFC_OBJ := hfc_2bs0.o +endif + +ifeq ($(CONFIG_HISAX_SEDLBAUER),y) + O_OBJS += sedlbauer.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o + ISAR_OBJ := isar.o endif +ifeq ($(CONFIG_HISAX_SPORTSTER),y) + O_OBJS += sportster.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_MIC),y) + O_OBJS += mic.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +ifeq ($(CONFIG_HISAX_NETJET),y) + O_OBJS += netjet.o + ISAC_OBJ := isac.o +endif + +ifeq ($(CONFIG_HISAX_TELES3C),y) + O_OBJS += teles3c.o + HFC_2BDS0 := hfc_2bds0.o +endif + +ifeq ($(CONFIG_HISAX_NICCY),y) + O_OBJS += niccy.o + ISAC_OBJ := isac.o + HSCX_OBJ := hscx.o +endif + +O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(ARCOFI_OBJ) +O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) +OX_OBJS += config.o + O_TARGET := + ifeq ($(CONFIG_ISDN_DRV_HISAX),y) O_TARGET += hisax.o else @@ -51,4 +154,14 @@ endif endif + 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 + +CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) + +cert.o: $(MD5FILES) md5sums.asc + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -D CERTIFICATION=$(CERT) -c -o cert.o cert.c + diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/arcofi.c linux/drivers/isdn/hisax/arcofi.c --- v2.0.35/linux/drivers/isdn/hisax/arcofi.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/arcofi.c Sun Nov 15 10:32:57 1998 @@ -0,0 +1,76 @@ +/* $Id: arcofi.c,v 1.1.2.5 1998/09/30 22:20:03 keil Exp $ + + * arcofi.c Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * + * $Log: arcofi.c,v $ + * Revision 1.1.2.5 1998/09/30 22:20:03 keil + * Cosmetics + * + * Revision 1.1.2.4 1998/09/27 13:05:29 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.1.2.3 1998/05/27 18:04:48 keil + * HiSax 3.0 + * + * Revision 1.1.2.2 1998/04/11 18:45:13 keil + * New interface + * + * Revision 1.1.2.1 1997/11/15 18:57:37 keil + * ARCOFI 2165 support + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isdnl1.h" +#include "isac.h" + +int +send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive) { + u_char val; + long flags; + int cnt=30; + + cs->mon_txp = 0; + cs->mon_txc = msg[0]; + memcpy(cs->mon_tx, &msg[1], cs->mon_txc); + switch(bc) { + case 0: break; + case 1: cs->mon_tx[1] |= 0x40; + break; + default: break; + } + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags); + if (receive) + test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags); + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + val = cs->readisac(cs, ISAC_MOSR); + cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]); + cs->mocr |= 0x10; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + save_flags(flags); + sti(); + while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) { + cnt--; + udelay(500); + } + if (receive) { + while (cnt && !test_bit(HW_MON1_RX_END, &cs->HW_Flags)) { + cnt--; + udelay(500); + } + } + restore_flags(flags); + if (cnt <= 0) { + printk(KERN_WARNING"HiSax arcofi monitor timed out\n"); + debugl1(cs, "HiSax arcofi monitor timed out"); + } + return(cnt); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/arcofi.h linux/drivers/isdn/hisax/arcofi.h --- v2.0.35/linux/drivers/isdn/hisax/arcofi.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/arcofi.h Sun Nov 15 10:32:57 1998 @@ -0,0 +1,23 @@ +/* $Id: arcofi.h,v 1.1.2.3 1998/05/27 18:04:50 keil Exp $ + + * arcofi.h Ansteuerung ARCOFI 2165 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: arcofi.h,v $ + * Revision 1.1.2.3 1998/05/27 18:04:50 keil + * HiSax 3.0 + * + * Revision 1.1.2.2 1998/04/11 18:45:14 keil + * New interface + * + * Revision 1.1.2.1 1997/11/15 18:57:38 keil + * ARCOFI 2165 support + * + * + */ + +#define ARCOFI_USE 1 + +extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/asuscom.c linux/drivers/isdn/hisax/asuscom.c --- v2.0.35/linux/drivers/isdn/hisax/asuscom.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/asuscom.c Sun Nov 15 10:32:57 1998 @@ -0,0 +1,407 @@ +/* $Id: asuscom.c,v 1.1.2.4 1998/11/03 00:05:42 keil Exp $ + + * asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Thanks to ASUSCOM NETWORK INC. Taiwan and Dynalink NL for informations + * + * + * $Log: asuscom.c,v $ + * Revision 1.1.2.4 1998/11/03 00:05:42 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.3 1998/06/18 23:10:26 keil + * Support for new IPAC card + * + * Revision 1.1.2.2 1998/04/08 21:58:37 keil + * New init code + * + * Revision 1.1.2.1 1998/01/27 22:34:02 keil + * dynalink ----> asuscom + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *Asuscom_revision = "$Revision: 1.1.2.4 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ASUS_ISAC 0 +#define ASUS_HSCX 1 +#define ASUS_ADR 2 +#define ASUS_CTRL_U7 3 +#define ASUS_CTRL_POTS 5 + +#define ASUS_IPAC_ALE 0 +#define ASUS_IPAC_DATA 1 + +#define ASUS_ISACHSCX 1 +#define ASUS_IPAC 2 + +/* CARD_ADR (Write) */ +#define ASUS_RESET 0x80 /* Bit 7 Reset-Leitung */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size); +} + +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80)); +} + +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value); +} + +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size); +} + +static void +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.asus.adr, + cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.asus.adr, + cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \ + cs->hw.asus.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \ + cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \ + cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \ + cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0); + } +} + +static void +asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista, val, icnt = 20; + + if (!cs) { + printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n"); + return; + } + ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA); +Start_IPAC: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + hscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "ASUS IRQ LOOP\n"); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0); +} + +void +release_io_asuscom(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.asus.cfg_reg) + release_region(cs->hw.asus.cfg_reg, bytecnt); +} + +static void +reset_asuscom(struct IsdnCardState *cs) +{ + long flags; + + if (cs->subtyp == ASUS_IPAC) + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20); + else + byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + if (cs->subtyp == ASUS_IPAC) + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0); + else + byteout(cs->hw.asus.adr, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + if (cs->subtyp == ASUS_IPAC) { + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0); + writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12); + } + restore_flags(flags); +} + +static int +Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_asuscom(cs); + return(0); + case CARD_RELEASE: + release_io_asuscom(cs); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == ASUS_IPAC) + return(request_irq(cs->irq, &asuscom_interrupt_ipac, + I4L_IRQ_FLAG, "HiSax", cs)); + else + return(request_irq(cs->irq, &asuscom_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + cs->debug |= L1_DEB_IPAC; + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_asuscom(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + u_char val; + char tmp[64]; + + strcpy(tmp, Asuscom_revision); + printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_ASUSCOM) + return (0); + + bytecnt = 8; + cs->hw.asus.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (check_region((cs->hw.asus.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.asus.cfg_reg, + cs->hw.asus.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn"); + } + printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n", + cs->hw.asus.cfg_reg, cs->irq); + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Asus_card_msg; + val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE, + cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID); + if (val == 1) { + cs->subtyp = ASUS_IPAC; + cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE; + cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA; + cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + printk(KERN_INFO "Asus: IPAC version %x\n", val); + } else { + cs->subtyp = ASUS_ISACHSCX; + cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR; + cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC; + cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX; + cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7; + cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS; + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + ISACVersion(cs, "ISDNLink:"); + if (HscxVersion(cs, "ISDNLink:")) { + printk(KERN_WARNING + "ISDNLink: wrong HSCX versions check IO address\n"); + release_io_asuscom(cs); + return (0); + } + } + printk(KERN_INFO "ISDNLink: resetting card\n"); + reset_asuscom(cs); + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/avm_a1.c linux/drivers/isdn/hisax/avm_a1.c --- v2.0.35/linux/drivers/isdn/hisax/avm_a1.c Tue Aug 5 09:00:12 1997 +++ linux/drivers/isdn/hisax/avm_a1.c Sun Nov 15 10:32:57 1998 @@ -1,11 +1,45 @@ -/* $Id: avm_a1.c,v 1.6 1997/04/13 19:54:07 keil Exp $ +/* $Id: avm_a1.c,v 1.6.2.12 1998/11/03 00:05:44 keil Exp $ * avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * * * $Log: avm_a1.c,v $ + * Revision 1.6.2.12 1998/11/03 00:05:44 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.6.2.11 1998/09/27 13:05:30 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.6.2.10 1998/05/27 18:04:50 keil + * HiSax 3.0 + * + * Revision 1.6.2.9 1998/04/08 21:58:39 keil + * New init code + * + * Revision 1.6.2.8 1998/01/27 22:37:49 keil + * fast io + * + * Revision 1.6.2.7 1998/01/13 23:06:11 keil + * really disable internal timer + * + * Revision 1.6.2.6 1998/01/02 06:49:01 calle + * Perodic timer of A1 now disabled, no need for linux driver. + * + * Revision 1.6.2.5 1997/11/15 18:50:41 keil + * new common init function + * + * Revision 1.6.2.4 1997/10/17 22:13:29 keil + * update to last hisax version + * + * Revision 2.1 1997/07/27 21:47:13 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:48 keil + * New Layer and card interface + * * Revision 1.6 1997/04/13 19:54:07 keil * Change in IRQ check delay for SMP * @@ -27,17 +61,20 @@ * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "avm_a1.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include extern const char *CardType[]; -const char *avm_revision = "$Revision: 1.6 $"; +static const char *avm_revision = "$Revision: 1.6.2.12 $"; + +#define AVM_A1_STAT_ISAC 0x01 +#define AVM_A1_STAT_HSCX 0x02 +#define AVM_A1_STAT_TIMER 0x04 -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readreg(unsigned int adr, u_char off) @@ -55,906 +92,299 @@ static inline void read_fifo(unsigned int adr, u_char * data, int size) { - insb(adr - 0x400, data, size); + insb(adr, data, size); } static void write_fifo(unsigned int adr, u_char * data, int size) { - outsb(adr - 0x400, data, size); + outsb(adr, data, size); } -static inline void -waitforCEC(int adr) -{ - int to = 50; - - while ((readreg(adr, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "AVM A1: waitforCEC timeout\n"); -} - - -static inline void -waitforXFW(int adr) -{ - int to = 50; - - while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "AVM A1: waitforXFW timeout\n"); -} +/* Interface functions */ -static inline void -writehscxCMDR(int adr, u_char data) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr); - writereg(adr, HSCX_CMDR, data); - restore_flags(flags); + return (readreg(cs->hw.avm.isac, offset)); } -/* - * fast interrupt here - */ - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR)); -} - -void -avm_a1_report(struct IsdnCardState *sp) -{ - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + writereg(cs->hw.avm.isac, offset, value); } -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + read_fifo(cs->hw.avm.isacfifo, data, size); } static void -hscx_fill_fifo(struct HscxState *hsp) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx]); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo(cs->hw.avm.isacfifo, data, size); } -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!(r & 0x80)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!(r & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - } else { - count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "AVM: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + return (readreg(cs->hw.avm.hscx[hscx], offset)); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.avm.hscx[hscx], offset, value); } -static void -isac_fill_fifo(struct IsdnCardState *sp) -{ - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static void -ph_command(struct IsdnCardState *sp, unsigned int command) -{ - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CIX0, (command << 2) | 3); -} - - -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readreg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writereg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = readreg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readreg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readreg(sp->hscx[1], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readreg(sp->hscx[0], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readreg(sp->hscx[0], HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, sval, stat = 0; - char tmp[32]; - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "AVM A1: Spurious interrupt!\n"); return; } - while (((sval = bytein(sp->cfg_reg)) & 0xf) != 0x7) { + while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) { if (!(sval & AVM_A1_STAT_TIMER)) { - byteout(sp->cfg_reg, 0x14); - byteout(sp->cfg_reg, 0x18); - sval = bytein(sp->cfg_reg); - } else if (sp->debug & L1_DEB_INTSTAT) { - sprintf(tmp, "avm IntStatus %x", sval); - debugl1(sp, tmp); - } + byteout(cs->hw.avm.cfg_reg, 0x1E); + sval = bytein(cs->hw.avm.cfg_reg); + } else if (cs->debug & L1_DEB_INTSTAT) + debugl1(cs, "avm IntStatus %x", sval); if (!(sval & AVM_A1_STAT_HSCX)) { - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA); if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } } if (!(sval & AVM_A1_STAT_ISAC)) { - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.avm.isac, ISAC_ISTA); if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } } } if (stat & 1) { - writereg(sp->hscx[0], HSCX_MASK, 0xFF); - writereg(sp->hscx[1], HSCX_MASK, 0xFF); - writereg(sp->hscx[0], HSCX_MASK, 0x0); - writereg(sp->hscx[1], HSCX_MASK, 0x0); + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0); } if (stat & 2) { - writereg(sp->isac, ISAC_MASK, 0xFF); - writereg(sp->isac, ISAC_MASK, 0x0); + writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.avm.isac, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) -{ - unsigned int adr = sp->isac; - - /* 16.3 IOM 2 Mode */ - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_ADF2, 0x80); - writereg(adr, ISAC_SQXR, 0x2f); - writereg(adr, ISAC_SPCR, 0x0); - writereg(adr, ISAC_ADF1, 0x2); - writereg(adr, ISAC_STCR, 0x70); - writereg(adr, ISAC_MODE, 0xc9); - writereg(adr, ISAC_TIMR, 0x0); - writereg(adr, ISAC_ADF1, 0x0); - writereg(adr, ISAC_CMDR, 0x41); - writereg(adr, ISAC_CIX0, (1 << 2) | 3); - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writereg(sp->hscx[hscx], HSCX_CCR1, 0x85); - writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF); - writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF); - writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF); - writereg(sp->hscx[hscx], HSCX_XBCH, 0x0); - writereg(sp->hscx[hscx], HSCX_RLCR, 0x0); - - switch (mode) { - case (0): - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0xff); - writereg(sp->hscx[hscx], HSCX_TSAR, 0xff); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - writereg(sp->hscx[hscx], HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0xe4); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0x8c); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - } - writereg(sp->hscx[hscx], HSCX_ISTA, 0x00); -} - inline static void -release_ioregs(struct IsdnCard *card, int mask) +release_ioregs(struct IsdnCardState *cs, int mask) { - release_region(card->sp->cfg_reg, 8); + release_region(cs->hw.avm.cfg_reg, 8); if (mask & 1) - release_region(card->sp->isac, 32); + release_region(cs->hw.avm.isac + 32, 32); if (mask & 2) - release_region(card->sp->isac - 0x400, 1); + release_region(cs->hw.avm.isacfifo, 1); if (mask & 4) - release_region(card->sp->hscx[0], 32); + release_region(cs->hw.avm.hscx[0] + 32, 32); if (mask & 8) - release_region(card->sp->hscx[0] - 0x400, 1); + release_region(cs->hw.avm.hscxfifo[0], 1); if (mask & 0x10) - release_region(card->sp->hscx[1], 32); + release_region(cs->hw.avm.hscx[1] + 32, 32); if (mask & 0x20) - release_region(card->sp->hscx[1] - 0x400, 1); + release_region(cs->hw.avm.hscxfifo[1], 1); } -void -release_io_avm_a1(struct IsdnCard *card) -{ - release_ioregs(card, 0x3f); -} - -static void -clear_pending_ints(struct IsdnCardState *sp) +static int +AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int val; - char tmp[64]; - - val = readreg(sp->hscx[1], HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->hscx[1], HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readreg(sp->hscx[0], HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readreg(sp->hscx[0], HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[1], HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[0], HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->isac, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readreg(sp->isac, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_MASK, 0); - writereg(sp->isac, ISAC_CMDR, 0x41); -} - -int -initavm_a1(struct IsdnCardState *sp) -{ - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat.interrupts[sp->irq]; - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &avm_a1_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat.interrupts[sp->irq] > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat.interrupts[sp->irq]); - debugl1(sp, tmp); - if (kstat.interrupts[sp->irq] == sp->counter) { - printk(KERN_WARNING - "AVM A1: IRQ(%d) getting no interrupts during init\n", - sp->irq); - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); - } + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_ioregs(cs, 0x3f); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &avm_a1_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 1); + byteout(cs->hw.avm.cfg_reg, 0x16); + byteout(cs->hw.avm.cfg_reg, 0x1E); + inithscxisac(cs, 2); + return(0); + case CARD_TEST: + return(0); } - return (ret); + return(0); } -int -setup_avm_a1(struct IsdnCard *card) +__initfunc(int +setup_avm_a1(struct IsdnCard *card)) { - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; + u_char val; + struct IsdnCardState *cs = card->cs; long flags; char tmp[64]; strcpy(tmp, avm_revision); - printk(KERN_NOTICE "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); - if (sp->typ != ISDN_CTYPE_A1) + printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_A1) return (0); - sp->cfg_reg = card->para[1] + 0x1800; - sp->isac = card->para[1] + 0x1400; - sp->hscx[0] = card->para[1] + 0x400; - sp->hscx[1] = card->para[1] + 0xc00; - sp->irq = card->para[0]; - if (check_region((sp->cfg_reg), 8)) { + cs->hw.avm.cfg_reg = card->para[1] + 0x1800; + cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20; + cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20; + cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20; + cs->hw.avm.isacfifo = card->para[1] + 0x1000; + cs->hw.avm.hscxfifo[0] = card->para[1]; + cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800; + cs->irq = card->para[0]; + if (check_region((cs->hw.avm.cfg_reg), 8)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); + cs->hw.avm.cfg_reg, + cs->hw.avm.cfg_reg + 8); return (0); } else { - request_region(sp->cfg_reg, 8, "avm cfg"); + request_region(cs->hw.avm.cfg_reg, 8, "avm cfg"); } - if (check_region((sp->isac), 32)) { + if (check_region((cs->hw.avm.isac + 32), 32)) { printk(KERN_WARNING "HiSax: %s isac ports %x-%x already in use\n", - CardType[sp->typ], - sp->isac, - sp->isac + 32); - release_ioregs(card, 0); + CardType[cs->typ], + cs->hw.avm.isac + 32, + cs->hw.avm.isac + 64); + release_ioregs(cs, 0); return (0); } else { - request_region(sp->isac, 32, "HiSax isac"); + request_region(cs->hw.avm.isac + 32, 32, "HiSax isac"); } - if (check_region((sp->isac - 0x400), 1)) { + if (check_region((cs->hw.avm.isacfifo), 1)) { printk(KERN_WARNING "HiSax: %s isac fifo port %x already in use\n", - CardType[sp->typ], - sp->isac - 0x400); - release_ioregs(card, 1); + CardType[cs->typ], + cs->hw.avm.isacfifo); + release_ioregs(cs, 1); return (0); } else { - request_region(sp->isac - 0x400, 1, "HiSax isac fifo"); + request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo"); } - if (check_region((sp->hscx[0]), 32)) { + if (check_region((cs->hw.avm.hscx[0]) + 32, 32)) { printk(KERN_WARNING "HiSax: %s hscx A ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 32); - release_ioregs(card, 3); + CardType[cs->typ], + cs->hw.avm.hscx[0] + 32, + cs->hw.avm.hscx[0] + 64); + release_ioregs(cs, 3); return (0); } else { - request_region(sp->hscx[0], 32, "HiSax hscx A"); + request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A"); } - if (check_region((sp->hscx[0] - 0x400), 1)) { + if (check_region(cs->hw.avm.hscxfifo[0], 1)) { printk(KERN_WARNING "HiSax: %s hscx A fifo port %x already in use\n", - CardType[sp->typ], - sp->hscx[0] - 0x400); - release_ioregs(card, 7); + CardType[cs->typ], + cs->hw.avm.hscxfifo[0]); + release_ioregs(cs, 7); return (0); } else { - request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo"); + request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo"); } - if (check_region((sp->hscx[1]), 32)) { + if (check_region(cs->hw.avm.hscx[1] + 32, 32)) { printk(KERN_WARNING "HiSax: %s hscx B ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[1], - sp->hscx[1] + 32); - release_ioregs(card, 0xf); + CardType[cs->typ], + cs->hw.avm.hscx[1] + 32, + cs->hw.avm.hscx[1] + 64); + release_ioregs(cs, 0xf); return (0); } else { - request_region(sp->hscx[1], 32, "HiSax hscx B"); + request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B"); } - if (check_region((sp->hscx[1] - 0x400), 1)) { + if (check_region(cs->hw.avm.hscxfifo[1], 1)) { printk(KERN_WARNING "HiSax: %s hscx B fifo port %x already in use\n", - CardType[sp->typ], - sp->hscx[1] - 0x400); - release_ioregs(card, 0x1f); + CardType[cs->typ], + cs->hw.avm.hscxfifo[1]); + release_ioregs(cs, 0x1f); return (0); } else { - request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo"); + request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo"); } save_flags(flags); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); sti(); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x1); + byteout(cs->hw.avm.cfg_reg, 0x1); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); HZDELAY(HZ / 5 + 1); - val = sp->irq; + val = cs->irq; if (val == 9) val = 2; - byteout(sp->cfg_reg + 1, val); + byteout(cs->hw.avm.cfg_reg + 1, val); HZDELAY(HZ / 5 + 1); - byteout(sp->cfg_reg, 0x0); + byteout(cs->hw.avm.cfg_reg, 0x0); HZDELAY(HZ / 5 + 1); restore_flags(flags); - val = bytein(sp->cfg_reg); + val = bytein(cs->hw.avm.cfg_reg); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg, val); - val = bytein(sp->cfg_reg + 3); + cs->hw.avm.cfg_reg, val); + val = bytein(cs->hw.avm.cfg_reg + 3); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg + 3, val); - val = bytein(sp->cfg_reg + 2); + cs->hw.avm.cfg_reg + 3, val); + val = bytein(cs->hw.avm.cfg_reg + 2); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg + 2, val); - byteout(sp->cfg_reg, 0x14); - byteout(sp->cfg_reg, 0x18); - val = bytein(sp->cfg_reg); + cs->hw.avm.cfg_reg + 2, val); + val = bytein(cs->hw.avm.cfg_reg); printk(KERN_INFO "AVM A1: Byte at %x is %x\n", - sp->cfg_reg, val); + cs->hw.avm.cfg_reg, val); - printk(KERN_NOTICE - "HiSax: %s config irq:%d cfg:%x\n", - CardType[sp->typ], sp->irq, - sp->cfg_reg); - printk(KERN_NOTICE - "HiSax: isac:%x/%x\n", - sp->isac, sp->isac - 0x400); - printk(KERN_NOTICE - "HiSax: hscx A:%x/%x hscx B:%x/%x\n", - sp->hscx[0], sp->hscx[0] - 0x400, - sp->hscx[1], sp->hscx[1] - 0x400); - verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf; - verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf; - printk(KERN_INFO "AVM A1: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readreg(sp->isac, ISAC_RBCH); - printk(KERN_INFO "AVM A1: ISAC %s\n", - ISACVersion(val)); - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + printk(KERN_INFO + "HiSax: %s config irq:%d cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.avm.cfg_reg); + printk(KERN_INFO + "HiSax: isac:0x%X/0x%X\n", + cs->hw.avm.isac + 32, cs->hw.avm.isacfifo); + printk(KERN_INFO + "HiSax: hscx A:0x%X/0x%X hscx B:0x%X/0x%X\n", + cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0], + cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &AVM_card_msg; + ISACVersion(cs, "AVM A1:"); + if (HscxVersion(cs, "AVM A1:")) { printk(KERN_WARNING "AVM A1: wrong HSCX versions check IO address\n"); - release_io_avm_a1(card); + release_ioregs(cs, 0x3f); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/avm_a1p.c linux/drivers/isdn/hisax/avm_a1p.c --- v2.0.35/linux/drivers/isdn/hisax/avm_a1p.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/avm_a1p.c Sun Nov 15 10:32:57 1998 @@ -0,0 +1,331 @@ +/* $Id: avm_a1p.c,v 1.1.2.3 1998/11/03 00:05:47 keil Exp $ + * + * avm_a1p.c low level stuff for the following AVM cards: + * A1 PCMCIA + * FRITZ!Card PCMCIA + * FRITZ!Card PCMCIA 2.0 + * + * Author Carsten Paeth (calle@calle.in-berlin.de) + * + * $Log: avm_a1p.c,v $ + * Revision 1.1.2.3 1998/11/03 00:05:47 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.2 1998/09/27 13:05:33 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.1.2.1 1998/07/15 14:43:26 calle + * Support for AVM passive PCMCIA cards: + * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +/* register offsets */ +#define ADDRREG_OFFSET 0x02 +#define DATAREG_OFFSET 0x03 +#define ASL0_OFFSET 0x04 +#define ASL1_OFFSET 0x05 +#define MODREG_OFFSET 0x06 +#define VERREG_OFFSET 0x07 + +/* address offsets */ +#define ISAC_FIFO_OFFSET 0x00 +#define ISAC_REG_OFFSET 0x20 +#define HSCX_CH_DIFF 0x40 +#define HSCX_FIFO_OFFSET 0x80 +#define HSCX_REG_OFFSET 0xa0 + +/* read bits ASL0 */ +#define ASL0_R_TIMER 0x10 /* active low */ +#define ASL0_R_ISAC 0x20 /* active low */ +#define ASL0_R_HSCX 0x40 /* active low */ +#define ASL0_R_TESTBIT 0x80 +#define ASL0_R_IRQPENDING (ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER) + +/* write bits ASL0 */ +#define ASL0_W_RESET 0x01 +#define ASL0_W_TDISABLE 0x02 +#define ASL0_W_TRESET 0x04 +#define ASL0_W_IRQENABLE 0x08 +#define ASL0_W_TESTBIT 0x80 + +/* write bits ASL1 */ +#define ASL1_W_LED0 0x10 +#define ASL1_W_LED1 0x20 +#define ASL1_W_ENABLE_S0 0xC0 + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static const char *avm_revision = "$Revision: 1.1.2.3 $"; + +static inline u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + long flags; + u_char ret; + + offset -= 0x20; + save_flags(flags); + cli(); + byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset); + ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET); + restore_flags(flags); + return ret; +} + +static inline void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + long flags; + + offset -= 0x20; + + save_flags(flags); + cli(); + byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset); + byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value); + restore_flags(flags); +} + +static inline void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + long flags; + + save_flags(flags); + cli(); + byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET); + insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size); + restore_flags(flags); +} + +static inline void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + long flags; + + save_flags(flags); + cli(); + byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET); + outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size); + restore_flags(flags); +} + +static inline u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + u_char ret; + long flags; + + offset -= 0x20; + + save_flags(flags); + cli(); + byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET, + HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset); + ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET); + restore_flags(flags); + return ret; +} + +static inline void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + long flags; + + offset -= 0x20; + + save_flags(flags); + cli(); + byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET, + HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset); + byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value); + restore_flags(flags); +} + +static inline void +ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size) +{ + long flags; + + save_flags(flags); + cli(); + byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET, + HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF); + insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size); + restore_flags(flags); +} + +static inline void +WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size) +{ + long flags; + + save_flags(flags); + cli(); + byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET, + HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF); + outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size); + restore_flags(flags); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt) + +#include "hscx_irq.c" + +static void +avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval, stat = 0; + + if (!cs) { + printk(KERN_WARNING "AVM A1 PCMCIA: Spurious interrupt!\n"); + return; + } + while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) { + if (cs->debug & L1_DEB_INTSTAT) + debugl1(cs, "avm IntStatus %x", sval); + if (sval & ASL0_R_HSCX) { + val = ReadHSCX(cs, 1, HSCX_ISTA); + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + } + if (sval & ASL0_R_ISAC) { + val = ReadISAC(cs, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + } + } + if (stat & 1) { + WriteHSCX(cs, 0, HSCX_MASK, 0xff); + WriteHSCX(cs, 1, HSCX_MASK, 0xff); + WriteHSCX(cs, 0, HSCX_MASK, 0x00); + WriteHSCX(cs, 1, HSCX_MASK, 0x00); + } + if (stat & 2) { + WriteISAC(cs, ISAC_MASK, 0xff); + WriteISAC(cs, ISAC_MASK, 0x00); + } +} + +static int +AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + int ret; + switch (mt) { + case CARD_RESET: + byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); + HZDELAY(HZ / 5 + 1); + byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET); + HZDELAY(HZ / 5 + 1); + byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); + return 0; + + case CARD_RELEASE: + /* free_irq is done in HiSax_closecard(). */ + /* free_irq(cs->irq, cs); */ + return 0; + + case CARD_SETIRQ: + ret = request_irq(cs->irq, &avm_a1p_interrupt, + I4L_IRQ_FLAG, "HiSax", cs); + if (ret) + return ret; + byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, + ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE); + return 0; + + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + inithscxisac(cs, 1); + inithscxisac(cs, 2); + return 0; + + case CARD_TEST: + /* we really don't need it for the PCMCIA Version */ + return 0; + + default: + /* all card drivers ignore others, so we do the same */ + return 0; + } + return 0; +} + +__initfunc(int +setup_avm_a1_pcmcia(struct IsdnCard *card)) +{ + u_char model, vers; + struct IsdnCardState *cs = card->cs; + long flags; + char tmp[64]; + + + strcpy(tmp, avm_revision); + printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n", + HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_A1_PCMCIA) + return (0); + + cs->hw.avm.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + + + save_flags(flags); + outb(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0); + sti(); + + byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); + HZDELAY(HZ / 5 + 1); + byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET); + HZDELAY(HZ / 5 + 1); + byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00); + + byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET); + + restore_flags(flags); + + model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET); + vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET); + + printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n", + cs->hw.avm.cfg_reg, cs->irq, model, vers); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &AVM_card_msg; + + ISACVersion(cs, "AVM A1 PCMCIA:"); + if (HscxVersion(cs, "AVM A1 PCMCIA:")) { + printk(KERN_WARNING + "AVM A1 PCMCIA: wrong HSCX versions check IO address\n"); + return (0); + } + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/avm_pci.c linux/drivers/isdn/hisax/avm_pci.c --- v2.0.35/linux/drivers/isdn/hisax/avm_pci.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/avm_pci.c Sun Nov 15 10:32:57 1998 @@ -0,0 +1,880 @@ +/* $Id: avm_pci.c,v 1.1.2.8 1998/11/05 21:11:12 keil Exp $ + + * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards + * Thanks to AVM, Berlin for informations + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: avm_pci.c,v $ + * Revision 1.1.2.8 1998/11/05 21:11:12 keil + * AVM PnP support + * + * Revision 1.1.2.7 1998/11/03 00:05:48 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.6 1998/10/16 12:46:03 keil + * fix pci detection for more as one card + * + * Revision 1.1.2.5 1998/10/13 18:38:50 keil + * Fix PCI detection + * + * Revision 1.1.2.4 1998/10/04 23:03:41 keil + * PCI has 255 device entries + * + * Revision 1.1.2.3 1998/09/27 23:52:57 keil + * Fix error handling + * + * Revision 1.1.2.2 1998/09/27 13:03:16 keil + * Fix segfaults on connect + * + * Revision 1.1.2.1 1998/08/25 14:01:24 calle + * Ported driver for AVM Fritz!Card PCI from the 2.1 tree. + * I could not test it. + * + * Revision 1.1 1998/08/20 13:47:30 keil + * first version + * + * + * + */ +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "isdnl1.h" +#include +#include +#include + +extern const char *CardType[]; +static const char *avm_pci_rev = "$Revision: 1.1.2.8 $"; + +#define AVM_FRITZ_PCI 1 +#define AVM_FRITZ_PNP 2 + +#define PCI_VENDOR_AVM 0x1244 +#define PCI_FRITZPCI_ID 0xa00 + +#define HDLC_FIFO 0x0 +#define HDLC_STATUS 0x4 + +#define AVM_HDLC_1 0x00 +#define AVM_HDLC_2 0x01 +#define AVM_ISAC_FIFO 0x02 +#define AVM_ISAC_REG_LOW 0x04 +#define AVM_ISAC_REG_HIGH 0x06 + +#define AVM_STATUS0_IRQ_ISAC 0x01 +#define AVM_STATUS0_IRQ_HDLC 0x02 +#define AVM_STATUS0_IRQ_TIMER 0x04 +#define AVM_STATUS0_IRQ_MASK 0x07 + +#define AVM_STATUS0_RESET 0x01 +#define AVM_STATUS0_DIS_TIMER 0x02 +#define AVM_STATUS0_RES_TIMER 0x04 +#define AVM_STATUS0_ENA_IRQ 0x08 +#define AVM_STATUS0_TESTBIT 0x10 + +#define AVM_STATUS1_INT_SEL 0x0f +#define AVM_STATUS1_ENA_IOM 0x80 + +#define HDLC_MODE_ITF_FLG 0x01 +#define HDLC_MODE_TRANS 0x02 +#define HDLC_MODE_CCR_7 0x04 +#define HDLC_MODE_CCR_16 0x08 +#define HDLC_MODE_TESTLOOP 0x80 + +#define HDLC_INT_XPR 0x80 +#define HDLC_INT_XDU 0x40 +#define HDLC_INT_RPR 0x20 +#define HDLC_INT_MASK 0xE0 + +#define HDLC_STAT_RME 0x01 +#define HDLC_STAT_RDO 0x10 +#define HDLC_STAT_CRCVFRRAB 0x0E +#define HDLC_STAT_CRCVFR 0x06 +#define HDLC_STAT_RML_MASK 0x3f00 + +#define HDLC_CMD_XRS 0x80 +#define HDLC_CMD_XME 0x01 +#define HDLC_CMD_RRS 0x20 +#define HDLC_CMD_XML_MASK 0x3f00 + + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; + register u_char val; + register long flags; + + save_flags(flags); + cli(); + outb(idx, cs->hw.avm.cfg_reg + 4); + val = inb(cs->hw.avm.isac + (offset & 0xf)); + restore_flags(flags); + return (val); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; + register long flags; + + save_flags(flags); + cli(); + outb(idx, cs->hw.avm.cfg_reg + 4); + outb(value, cs->hw.avm.isac + (offset & 0xf)); + restore_flags(flags); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4); + insb(cs->hw.avm.isac, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4); + outsb(cs->hw.avm.isac, data, size); +} + +static inline u_int +ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset) +{ + register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; + register u_int val; + register long flags; + + save_flags(flags); + cli(); + outl(idx, cs->hw.avm.cfg_reg + 4); + val = inl(cs->hw.avm.isac + offset); + restore_flags(flags); + return (val); +} + +static inline void +WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value) +{ + register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; + register long flags; + + save_flags(flags); + cli(); + outl(idx, cs->hw.avm.cfg_reg + 4); + outl(value, cs->hw.avm.isac + offset); + restore_flags(flags); +} + +static inline u_char +ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset) +{ + register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; + register u_char val; + register long flags; + + save_flags(flags); + cli(); + outb(idx, cs->hw.avm.cfg_reg + 4); + val = inb(cs->hw.avm.isac + offset); + restore_flags(flags); + return (val); +} + +static inline void +WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value) +{ + register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; + register long flags; + + save_flags(flags); + cli(); + outb(idx, cs->hw.avm.cfg_reg + 4); + outb(value, cs->hw.avm.isac + offset); + restore_flags(flags); +} + +static u_char +ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset) +{ + return(0xff & ReadHDLCPCI(cs, chan, offset)); +} + +static void +WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value) +{ + WriteHDLCPCI(cs, chan, offset, value); +} + +static inline +struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel) +{ + if (cs->bcs[0].mode && (cs->bcs[0].channel == channel)) + return(&cs->bcs[0]); + else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel)) + return(&cs->bcs[1]); + else + return(NULL); +} + +void inline +hdlc_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +write_ctrl(struct BCState *bcs, int which) { + + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "hdlc %c wr%x ctrl %x", + 'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl); + if (bcs->cs->subtyp == AVM_FRITZ_PCI) { + WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl); + } else { + if (which & 4) + WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2, + bcs->hw.hdlc.ctrl.sr.mode); + if (which & 2) + WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1, + bcs->hw.hdlc.ctrl.sr.xml); + if (which & 1) + WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS, + bcs->hw.hdlc.ctrl.sr.cmd); + } +} + +void +modehdlc(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int hdlc = bcs->channel; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hdlc %c mode %d ichan %d", + 'A' + hdlc, mode, bc); + bcs->mode = mode; + bcs->channel = bc; + bcs->hw.hdlc.ctrl.ctrl = 0; + switch (mode) { + case (L1_MODE_NULL): + bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; + write_ctrl(bcs, 5); + break; + case (L1_MODE_TRANS): + bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; + write_ctrl(bcs, 5); + bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS; + write_ctrl(bcs, 1); + bcs->hw.hdlc.ctrl.sr.cmd = 0; + hdlc_sched_event(bcs, B_XMTBUFREADY); + break; + case (L1_MODE_HDLC): + bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG; + write_ctrl(bcs, 5); + bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS; + write_ctrl(bcs, 1); + bcs->hw.hdlc.ctrl.sr.cmd = 0; + hdlc_sched_event(bcs, B_XMTBUFREADY); + break; + } +} + +static inline void +hdlc_empty_fifo(struct BCState *bcs, int count) +{ + register u_int *ptr; + u_char *p; + u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1; + int cnt=0; + struct IsdnCardState *cs = bcs->cs; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hdlc_empty_fifo %d", count); + if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hdlc_empty_fifo: incoming packet too large"); + return; + } + ptr = (u_int *) p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx; + bcs->hw.hdlc.rcvidx += count; + if (cs->subtyp == AVM_FRITZ_PCI) { + outl(idx, cs->hw.avm.cfg_reg + 4); + while (cnt < count) { + *ptr++ = inl(cs->hw.avm.isac); + cnt += 4; + } + } else { + outb(idx, cs->hw.avm.cfg_reg + 4); + while (cnt < count) { + *p++ = inb(cs->hw.avm.isac); + cnt++; + } + } + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + if (cs->subtyp == AVM_FRITZ_PNP) + p = (u_char *) ptr; + t += sprintf(t, "hdlc_empty_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', count); + QuickHex(t, p, count); + debugl1(cs, bcs->blog); + } +} + +static inline void +hdlc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int count, cnt =0; + int fifo_size = 32; + u_char *p; + u_int *ptr; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hdlc_fill_fifo"); + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME; + if (bcs->tx_skb->len > fifo_size) { + count = fifo_size; + } else { + count = bcs->tx_skb->len; + if (bcs->mode != L1_MODE_TRANS) + bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME; + } + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len); + ptr = (u_int *) p = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hdlc.count += count; + bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count); + write_ctrl(bcs, 3); /* sets the correct index too */ + if (cs->subtyp == AVM_FRITZ_PCI) { + while (cnthw.avm.isac); + cnt += 4; + } + } else { + while (cnthw.avm.isac); + cnt++; + } + } + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + if (cs->subtyp == AVM_FRITZ_PNP) + p = (u_char *) ptr; + t += sprintf(t, "hdlc_fill_fifo %c cnt %d", + bcs->channel ? 'B' : 'A', count); + QuickHex(t, p, count); + debugl1(cs, bcs->blog); + } +} + +static void +fill_hdlc(struct BCState *bcs) +{ + long flags; + save_flags(flags); + cli(); + hdlc_fill_fifo(bcs); + restore_flags(flags); +} + +static inline void +HDLC_irq(struct BCState *bcs, u_int stat) { + int len; + struct sk_buff *skb; + + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat); + if (stat & HDLC_INT_RPR) { + if (stat & HDLC_STAT_RDO) { + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "RDO"); + else + debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat); + bcs->hw.hdlc.ctrl.sr.xml = 0; + bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS; + write_ctrl(bcs, 1); + bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS; + write_ctrl(bcs, 1); + bcs->hw.hdlc.rcvidx = 0; + } else { + if (!(len = (stat & HDLC_STAT_RML_MASK)>>8)) + len = 32; + hdlc_empty_fifo(bcs, len); + if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) { + if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) || + (bcs->mode == L1_MODE_TRANS)) { + if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx))) + printk(KERN_WARNING "HDLC: receive out of memory\n"); + else { + memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx), + bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->hw.hdlc.rcvidx = 0; + hdlc_sched_event(bcs, B_RCVBUFREADY); + } else { + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "invalid frame"); + else + debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat); + bcs->hw.hdlc.rcvidx = 0; + } + } + } + } + if (stat & HDLC_INT_XDU) { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hdlc.count); + bcs->tx_cnt += bcs->hw.hdlc.count; + bcs->hw.hdlc.count = 0; +// hdlc_sched_event(bcs, B_XMTBUFREADY); + if (bcs->cs->debug & L1_DEB_WARN) + debugl1(bcs->cs, "ch%d XDU", bcs->channel); + } else if (bcs->cs->debug & L1_DEB_WARN) + debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel); + bcs->hw.hdlc.ctrl.sr.xml = 0; + bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS; + write_ctrl(bcs, 1); + bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS; + write_ctrl(bcs, 1); + hdlc_fill_fifo(bcs); + } else if (stat & HDLC_INT_XPR) { + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + hdlc_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hdlc.count); + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->hw.hdlc.count = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hdlc.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hdlc_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + hdlc_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +inline void +HDLC_irq_main(struct IsdnCardState *cs) +{ + u_int stat; + long flags; + struct BCState *bcs; + + save_flags(flags); + cli(); + if (cs->subtyp == AVM_FRITZ_PCI) { + stat = ReadHDLCPCI(cs, 0, HDLC_STATUS); + } else { + stat = ReadHDLCPnP(cs, 0, HDLC_STATUS); + if (stat & HDLC_INT_RPR) + stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8; + } + if (stat & HDLC_INT_MASK) { + if (!(bcs = Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hdlc spurious channel 0 IRQ"); + } else + HDLC_irq(bcs, stat); + } + if (cs->subtyp == AVM_FRITZ_PCI) { + stat = ReadHDLCPCI(cs, 1, HDLC_STATUS); + } else { + stat = ReadHDLCPnP(cs, 1, HDLC_STATUS); + if (stat & HDLC_INT_RPR) + stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8; + } + if (stat & HDLC_INT_MASK) { + if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hdlc spurious channel 1 IRQ"); + } else + HDLC_irq(bcs, stat); + } + restore_flags(flags); +} + +void +hdlc_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hdlc.count = 0; + restore_flags(flags); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->tx_skb = skb; + st->l1.bcs->hw.hdlc.count = 0; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + modehdlc(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + modehdlc(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +void +close_hdlcstate(struct BCState *bcs) +{ + modehdlc(bcs, 0, 0); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.hdlc.rcvbuf) { + kfree(bcs->hw.hdlc.rcvbuf); + bcs->hw.hdlc.rcvbuf = NULL; + } + if (bcs->blog) { + kfree(bcs->blog); + bcs->blog = NULL; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +int +open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hdlc.rcvbuf\n"); + return (1); + } + if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for bcs->blog\n"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + kfree(bcs->hw.hdlc.rcvbuf); + bcs->hw.hdlc.rcvbuf = NULL; + return (2); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->hw.hdlc.rcvidx = 0; + bcs->tx_cnt = 0; + return (0); +} + +int +setstack_hdlc(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_hdlcstate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hdlc_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +HISAX_INITFUNC(void +clear_pending_hdlc_ints(struct IsdnCardState *cs)) +{ + u_int val; + + if (cs->subtyp == AVM_FRITZ_PCI) { + val = ReadHDLCPCI(cs, 0, HDLC_STATUS); + debugl1(cs, "HDLC 1 STA %x", val); + val = ReadHDLCPCI(cs, 1, HDLC_STATUS); + debugl1(cs, "HDLC 2 STA %x", val); + } else { + val = ReadHDLCPnP(cs, 0, HDLC_STATUS); + debugl1(cs, "HDLC 1 STA %x", val); + val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1); + debugl1(cs, "HDLC 1 RML %x", val); + val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2); + debugl1(cs, "HDLC 1 MODE %x", val); + val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3); + debugl1(cs, "HDLC 1 VIN %x", val); + val = ReadHDLCPnP(cs, 1, HDLC_STATUS); + debugl1(cs, "HDLC 2 STA %x", val); + val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1); + debugl1(cs, "HDLC 2 RML %x", val); + val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2); + debugl1(cs, "HDLC 2 MODE %x", val); + val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3); + debugl1(cs, "HDLC 2 VIN %x", val); + } +} + +HISAX_INITFUNC(void +inithdlc(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_hdlc; + cs->bcs[1].BC_SetStack = setstack_hdlc; + cs->bcs[0].BC_Close = close_hdlcstate; + cs->bcs[1].BC_Close = close_hdlcstate; + modehdlc(cs->bcs, 0, 0); + modehdlc(cs->bcs + 1, 0, 0); +} + +static void +avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + u_char sval; + + if (!cs) { + printk(KERN_WARNING "AVM PCI: Spurious interrupt!\n"); + return; + } + sval = inb(cs->hw.avm.cfg_reg + 2); + if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) + /* possible a shared IRQ reqest */ + return; + if (!(sval & AVM_STATUS0_IRQ_ISAC)) { + val = ReadISAC(cs, ISAC_ISTA); + isac_interrupt(cs, val); + stat |= 2; + } + if (!(sval & AVM_STATUS0_IRQ_HDLC)) { + HDLC_irq_main(cs); + } + if (stat & 2) { + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); + } +} + +static void +reset_avmpcipnp(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "AVM PCI/PnP: reset\n"); + save_flags(flags); + sti(); + outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2); + outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3)); +} + +static int +AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + u_int irq_flag; + + switch (mt) { + case CARD_RESET: + reset_avmpcipnp(cs); + return(0); + case CARD_RELEASE: + outb(0, cs->hw.avm.cfg_reg + 2); + release_region(cs->hw.avm.cfg_reg, 32); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == AVM_FRITZ_PCI) + irq_flag = I4L_IRQ_FLAG | SA_SHIRQ; + else + irq_flag = I4L_IRQ_FLAG; + return(request_irq(cs->irq, &avm_pcipnp_interrupt, + irq_flag, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + initisac(cs); + clear_pending_hdlc_ints(cs); + inithdlc(cs); + outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER, + cs->hw.avm.cfg_reg + 2); + WriteISAC(cs, ISAC_MASK, 0); + outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | + AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2); + /* RESET Receiver and Transmitter */ + WriteISAC(cs, ISAC_CMDR, 0x41); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static int pci_index __initdata = 0; + +__initfunc(int +setup_avm_pcipnp(struct IsdnCard *card)) +{ + u_int val, ver; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, avm_pci_rev); + printk(KERN_INFO "HiSax: AVM PCI/ISAPnP driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_FRITZPCI) + return (0); + if (card->para[1]) { + cs->hw.avm.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = AVM_FRITZ_PNP; + } else { +#if CONFIG_PCI + for (; pci_index < 255; pci_index++) { + unsigned char pci_bus, pci_device_fn; + unsigned int ioaddr; + unsigned char irq; + + if (pcibios_find_device (PCI_VENDOR_AVM, + PCI_FRITZPCI_ID, pci_index, + &pci_bus, &pci_device_fn) != 0) { + continue; + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &ioaddr); + cs->irq = irq; + cs->hw.avm.cfg_reg = ioaddr & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.avm.cfg_reg) { + printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n"); + return(0); + } + cs->subtyp = AVM_FRITZ_PCI; + break; + } + if (pci_index == 255) { + printk(KERN_WARNING "FritzPCI: No PCI card found\n"); + return(0); + } + pci_index++; +#else + printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); + return (0); +#endif /* CONFIG_PCI */ + } + cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10; + if (check_region((cs->hw.avm.cfg_reg), 32)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.avm.cfg_reg, + cs->hw.avm.cfg_reg + 31); + return (0); + } else { + request_region(cs->hw.avm.cfg_reg, 32, + (cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP"); + } + switch (cs->subtyp) { + case AVM_FRITZ_PCI: + val = inl(cs->hw.avm.cfg_reg); + printk(KERN_INFO "AVM PCI: stat %#x\n", val); + printk(KERN_INFO "AVM PCI: Class %X Rev %d\n", + val & 0xff, (val>>8) & 0xff); + cs->BC_Read_Reg = &ReadHDLC_s; + cs->BC_Write_Reg = &WriteHDLC_s; + break; + case AVM_FRITZ_PNP: + val = inb(cs->hw.avm.cfg_reg); + ver = inb(cs->hw.avm.cfg_reg + 1); + printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver); + reset_avmpcipnp(cs); + cs->BC_Read_Reg = &ReadHDLCPnP; + cs->BC_Write_Reg = &WriteHDLCPnP; + break; + default: + printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp); + return(0); + } + printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n", + (cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP", + cs->irq, cs->hw.avm.cfg_reg); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Send_Data = &fill_hdlc; + cs->cardmsg = &AVM_card_msg; + ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:"); + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c --- v2.0.35/linux/drivers/isdn/hisax/callc.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/callc.c Sun Nov 15 10:32:58 1998 @@ -1,156 +1,146 @@ -/* $Id: callc.c,v 1.30 1997/05/29 10:40:43 keil Exp $ +/* $Id: callc.c,v 1.30.2.13 1998/11/05 21:13:32 keil Exp $ - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * * Thanks to Jan den Ouden * Fritz Elfert * * $Log: callc.c,v $ - * Revision 1.30 1997/05/29 10:40:43 keil - * chanp->impair was uninitialised - * - * Revision 1.29 1997/04/23 20:09:49 fritz - * Removed tmp, used by removed debugging code. - * - * Revision 1.28 1997/04/21 13:42:25 keil - * Remove unneeded debug - * - * Revision 1.27 1997/04/16 14:21:01 keil - * remove unused variable - * - * Revision 1.26 1997/04/13 19:55:21 keil - * Changes in debugging code - * - * Revision 1.25 1997/04/06 22:54:08 keil - * Using SKB's - * - * Revision 1.24 1997/03/05 11:28:03 keil - * fixed undefined l2tei procedure - * a layer1 release delete now the drel timer + * Revision 1.30.2.13 1998/11/05 21:13:32 keil + * minor fixes * - * Revision 1.23 1997/03/04 23:07:42 keil - * bugfix dial parameter + * Revision 1.30.2.12 1998/11/03 00:05:51 keil + * certification related changes + * fixed logging for smaller stack use * - * Revision 1.22 1997/02/27 13:51:55 keil - * Reset B-channel (dlc) statemachine in every release + * Revision 1.30.2.11 1998/09/30 22:20:05 keil + * Cosmetics * - * Revision 1.21 1997/02/19 09:24:27 keil - * Bugfix: Hangup to LL if a ttyI rings + * Revision 1.30.2.10 1998/09/27 13:05:35 keil + * Apply most changes from 2.1.X (HiSax 3.1) * - * Revision 1.20 1997/02/17 00:32:47 keil - * Bugfix: No Busy reported to LL + * Revision 1.30.2.9 1998/05/27 18:04:53 keil + * HiSax 3.0 * - * Revision 1.19 1997/02/14 12:23:10 fritz - * Added support for new insmod parameter handling. + * Revision 1.30.2.8 1998/04/11 18:48:26 keil + * remove debug * - * Revision 1.18 1997/02/11 01:36:58 keil - * Changed setup-interface (incoming and outgoing), cause reporting + * Revision 1.30.2.7 1998/04/08 21:51:50 keil + * new debug * - * Revision 1.17 1997/02/09 00:23:10 keil - * new interface handling, one interface per card - * some changes in debug and leased line mode + * Revision 1.30.2.6 1998/03/07 23:15:02 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.16 1997/01/27 23:17:03 keil - * delete timers while unloading + * Revision 1.30.2.5 1998/02/09 11:24:17 keil + * New leased line support (Read README.HiSax!) * - * Revision 1.15 1997/01/27 16:00:38 keil - * D-channel shutdown delay; improved callback + * Revision 1.30.2.4 1998/01/27 22:46:00 keil + * B-channel send delay now configurable * - * Revision 1.14 1997/01/21 22:16:39 keil - * new statemachine; leased line support; cleanup for 2.0 + * Revision 1.30.2.3 1998/01/11 23:21:03 keil + * Missing callc state * - * Revision 1.13 1996/12/08 19:51:17 keil - * bugfixes from Pekka Sarnila + * Revision 1.30.2.2 1997/11/15 18:54:31 keil + * cosmetics * - * Revision 1.12 1996/11/26 20:20:03 keil - * fixed warning while compile + * Revision 1.30.2.1 1997/10/17 22:13:32 keil + * update to last hisax version * - * Revision 1.11 1996/11/26 18:43:17 keil - * change ioctl 555 --> 55 (555 didn't work) + * Revision 2.6 1997/09/11 17:26:58 keil + * Open B-channel if here are incomming packets * - * Revision 1.10 1996/11/26 18:06:07 keil - * fixed missing break statement,ioctl 555 reset modcount + * Revision 2.5 1997/08/07 17:46:05 keil + * Fix Incomming Call without broadcast * - * Revision 1.9 1996/11/18 20:23:19 keil - * log writebuf channel not open changed + * Revision 2.4 1997/08/03 14:37:58 keil + * Activate Layer2 in PtP mode * - * Revision 1.8 1996/11/06 17:43:17 keil - * more changes for 2.1.X;block fixed ST_PRO_W + * Revision 2.3 1997/07/31 19:23:40 keil + * LAYER2_WATCHING for PtP * - * Revision 1.7 1996/11/06 15:13:51 keil - * typo 0x64 --->64 in debug code + * Revision 2.2 1997/07/31 11:48:18 keil + * experimental REJECT after ALERTING * - * Revision 1.6 1996/11/05 19:40:33 keil - * X.75 windowsize + * Revision 2.1 1997/07/30 17:12:59 keil + * more changes for 'One TEI per card' * - * Revision 1.5 1996/10/30 10:11:06 keil - * debugging LOCK changed;ST_REL_W EV_HANGUP added + * Revision 2.0 1997/07/27 21:12:21 keil + * CRef based L3; new channel handling; many other stuff * - * Revision 1.4 1996/10/27 22:20:16 keil - * alerting bugfixes - * no static b-channel<->channel mapping + * Revision 1.31 1997/06/26 11:09:23 keil + * New managment and minor changes * - * Revision 1.2 1996/10/16 21:29:45 keil - * compile bug as "not module" - * Callback with euro - * - * Revision 1.1 1996/10/13 20:04:50 keil - * Initial revision + * old logs removed /KKe * */ #define __NO_VERSION__ #include "hisax.h" +#include "../avmb1/capicmd.h" /* this should be moved in a common place */ #ifdef MODULE -#if (LINUX_VERSION_CODE < 0x020111) extern long mod_use_count_; #define MOD_USE_COUNT mod_use_count_ -#else -#define MOD_USE_COUNT ((&__this_module)->usecount) -#endif #endif /* MODULE */ -const char *l4_revision = "$Revision: 1.30 $"; +const char *lli_revision = "$Revision: 1.30.2.13 $"; extern struct IsdnCard cards[]; extern int nrcards; extern void HiSax_mod_dec_use_count(void); extern void HiSax_mod_inc_use_count(void); -static int init_ds(struct Channel *chanp, int incoming); -static void release_ds(struct Channel *chanp); +static int init_b_st(struct Channel *chanp, int incoming); +static void release_b_st(struct Channel *chanp); static struct Fsm callcfsm = -{NULL, 0, 0}; -static struct Fsm lcfsm = -{NULL, 0, 0}; +{NULL, 0, 0, NULL, NULL}; static int chancount = 0; -/* Flags for remembering action done in l4 */ +/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */ +#define ALERT_REJECT 0 + +/* Value to delay the sending of the first B-channel paket after CONNECT + * here is no value given by ITU, but experience shows that 300 ms will + * work on many networks, if you or your other side is behind local exchanges + * a greater value may be recommented. If the delay is to short the first paket + * will be lost and autodetect on many comercial routers goes wrong ! + * You can adjust this value on runtime with + * hisaxctrl 2 + * value is in milliseconds + */ +#define DEFAULT_B_DELAY 300 -#define FLG_START_D 0x0001 -#define FLG_ESTAB_D 0x0002 -#define FLG_CALL_SEND 0x0004 -#define FLG_CALL_REC 0x0008 -#define FLG_CALL_ALERT 0x0010 -#define FLG_START_B 0x0020 -#define FLG_CONNECT_B 0x0040 -#define FLG_LL_DCONN 0x0080 -#define FLG_LL_BCONN 0x0100 -#define FLG_DISC_SEND 0x0200 -#define FLG_DISC_REC 0x0400 -#define FLG_REL_REC 0x0800 +/* Flags for remembering action done in lli */ -#define SETBIT(flg, item) flg |= item -#define RESBIT(flg, item) flg &= (~item) +#define FLG_START_D 0 +#define FLG_ESTAB_D 1 +#define FLG_CALL_SEND 2 +#define FLG_CALL_REC 3 +#define FLG_CALL_ALERT 4 +#define FLG_START_B 5 +#define FLG_CONNECT_B 6 +#define FLG_LL_DCONN 7 +#define FLG_LL_BCONN 8 +#define FLG_DISC_SEND 9 +#define FLG_DISC_REC 10 +#define FLG_REL_REC 11 +#define FLG_DO_ALERT 12 +#define FLG_DO_HANGUP 13 +#define FLG_DO_CONNECT 14 +#define FLG_DO_ESTAB 15 +#define FLG_RESUME 16 /* * Because of callback it's a good idea to delay the shutdown of the d-channel */ -#define DREL_TIMER_VALUE 30000 +#define DREL_TIMER_VALUE 40000 /* * Find card with given driverId @@ -162,21 +152,36 @@ int i; for (i = 0; i < nrcards; i++) - if (cards[i].sp) - if (cards[i].sp->myid == driverid) - return (cards[i].sp); + if (cards[i].cs) + if (cards[i].cs->myid == driverid) + return (cards[i].cs); return (struct IsdnCardState *) 0; } +int +discard_queue(struct sk_buff_head *q) +{ + struct sk_buff *skb; + int ret=0; + + while ((skb = skb_dequeue(q))) { + dev_kfree_skb(skb, FREE_READ); + ret++; + } + return(ret); +} + static void -link_debug(struct Channel *chanp, char *s, int direction) +link_debug(struct Channel *chanp, int direction, char *fmt, ...) { - char tmp[100], tm[32]; + va_list args; + char tmp[16]; - jiftime(tm, jiffies); - sprintf(tmp, "%s Channel %d %s %s\n", tm, chanp->chan, - direction ? "LL->HL" : "HL->LL", s); - HiSax_putstatus(chanp->sp, tmp); + va_start(args, fmt); + sprintf(tmp, "Ch%d %s ", chanp->chan, + direction ? "LL->HL" : "HL->LL"); + VHiSax_putstatus(chanp->cs, tmp, fmt, args); + va_end(args); } @@ -233,7 +238,7 @@ EV_SETUP_CMPL_IND, /* 10 */ EV_BC_EST, /* 11 */ EV_WRITEBUF, /* 12 */ - EV_DATAIN, /* 13 */ + EV_ESTABLISH, /* 13 */ EV_HANGUP, /* 14 */ EV_BC_REL, /* 15 */ EV_CINF, /* 16 */ @@ -263,7 +268,7 @@ "EV_SETUP_CMPL_IND", "EV_BC_EST", "EV_WRITEBUF", - "EV_DATAIN", + "EV_ESTABLISH", "EV_HANGUP", "EV_BC_REL", "EV_CINF", @@ -276,1105 +281,1021 @@ "EV_RELEASE_ERR", }; -enum { - ST_LC_NULL, - ST_LC_ACTIVATE_WAIT, - ST_LC_DELAY, - ST_LC_ESTABLISH_WAIT, - ST_LC_CONNECTED, - ST_LC_FLUSH_WAIT, - ST_LC_FLUSH_DELAY, - ST_LC_RELEASE_WAIT, -}; - -#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1) - -static char *strLcState[] = +static inline void +lli_deliver_cause(struct Channel *chanp, isdn_ctrl *ic) { - "ST_LC_NULL", - "ST_LC_ACTIVATE_WAIT", - "ST_LC_DELAY", - "ST_LC_ESTABLISH_WAIT", - "ST_LC_CONNECTED", - "ST_LC_FLUSH_WAIT", - "ST_LC_FLUSH_DELAY", - "ST_LC_RELEASE_WAIT", -}; - -enum { - EV_LC_ESTABLISH, - EV_LC_PH_ACTIVATE, - EV_LC_PH_DEACTIVATE, - EV_LC_DL_ESTABLISH, - EV_LC_TIMER, - EV_LC_DL_FLUSH, - EV_LC_DL_RELEASE, - EV_LC_FLUSH, - EV_LC_RELEASE, -}; - -#define LC_EVENT_COUNT (EV_LC_RELEASE+1) + if (chanp->proc->para.cause < 0) + return; + ic->driver = chanp->cs->myid; + ic->command = ISDN_STAT_CAUSE; + ic->arg = chanp->chan; + if (chanp->cs->protocol == ISDN_PTYPE_EURO) + sprintf(ic->parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f, + chanp->proc->para.cause & 0x7f); + else + sprintf(ic->parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f, + chanp->proc->para.cause & 0x7f); + chanp->cs->iif.statcallb(ic); +} -static char *strLcEvent[] = +static void +lli_d_established(struct FsmInst *fi, int event, void *arg) { - "EV_LC_ESTABLISH", - "EV_LC_PH_ACTIVATE", - "EV_LC_PH_DEACTIVATE", - "EV_LC_DL_ESTABLISH", - "EV_LC_TIMER", - "EV_LC_DL_FLUSH", - "EV_LC_DL_RELEASE", - "EV_LC_FLUSH", - "EV_LC_RELEASE", -}; + struct Channel *chanp = fi->userdata; -#define LC_D 0 -#define LC_B 1 + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + if (chanp->leased) { + isdn_ctrl ic; + int ret; -static inline void -l4_deliver_cause(struct Channel *chanp) + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_IN_WAIT_LL); + test_and_set_bit(FLG_CALL_REC, &chanp->Flags); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_ICALL_LEASED"); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_ICALL; + ic.arg = chanp->chan; + ic.parm.setup.si1 = 7; + ic.parm.setup.si2 = 0; + ic.parm.setup.plan = 0; + ic.parm.setup.screen = 0; + sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1); + sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid); + ret = chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, 1, "statcallb ret=%d", ret); + if (!ret) { + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); + FsmChangeState(fi, ST_NULL); + } + } else if (fi->state == ST_WAIT_DSHUTDOWN) + FsmChangeState(fi, ST_NULL); +} + +static void +lli_d_released(struct FsmInst *fi, int event, void *arg) { - isdn_ctrl ic; + struct Channel *chanp = fi->userdata; - if (chanp->para.cause < 0) - return; - ic.driver = chanp->sp->myid; - ic.command = ISDN_STAT_CAUSE; - ic.arg = chanp->chan; - if (chanp->sp->protocol == ISDN_PTYPE_EURO) - sprintf(ic.parm.num, "E%02X%02X", chanp->para.loc & 0x7f, - chanp->para.cause & 0x7f); - else - sprintf(ic.parm.num, "%02X%02X", chanp->para.loc & 0x7f, - chanp->para.cause & 0x7f); - chanp->sp->iif.statcallb(&ic); + test_and_clear_bit(FLG_START_D, &chanp->Flags); } /* * Dial out */ static void -l4_prep_dialout(struct FsmInst *fi, int event, void *arg) +lli_prep_dialout(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_OUT_WAIT_D); FsmDelTimer(&chanp->drel_timer, 60); FsmDelTimer(&chanp->dial_timer, 73); - chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = 0; - chanp->lc_b.l2_start = !0; - switch (chanp->l2_active_protocol) { - case (ISDN_PROTO_L2_X75I): - chanp->lc_b.l2_establish = !0; - break; - case (ISDN_PROTO_L2_HDLC): - case (ISDN_PROTO_L2_TRANS): - chanp->lc_b.l2_establish = 0; - break; - default: - printk(KERN_WARNING "l4_prep_dialout unknown protocol\n"); - break; - } - if (chanp->Flags & FLG_ESTAB_D) { + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { FsmEvent(fi, EV_DLEST, NULL); } else { - chanp->Flags = FLG_START_D; - if (chanp->leased) { - chanp->lc_d.l2_establish = 0; - } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); + chanp->Flags = 0; + if (EV_RESUME == event) + test_and_set_bit(FLG_RESUME, &chanp->Flags); + test_and_set_bit(FLG_START_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); } } static void -l4_do_dialout(struct FsmInst *fi, int event, void *arg) +lli_do_dialout(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; + int ev; FsmChangeState(fi, ST_OUT_DIAL); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); + if (test_and_clear_bit(FLG_RESUME, &chanp->Flags)) + ev = CC_RESUME | REQUEST; + else + ev = CC_SETUP | REQUEST; if (chanp->leased) { - chanp->para.bchannel = (chanp->chan & 1) + 1; FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); } else { - SETBIT(chanp->Flags, FLG_ESTAB_D); - chanp->para.callref = chanp->outcallref; - chanp->outcallref++; - if (chanp->outcallref == 128) - chanp->outcallref = 64; - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL); - SETBIT(chanp->Flags, FLG_CALL_SEND); + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, ev, chanp); + test_and_set_bit(FLG_CALL_SEND, &chanp->Flags); } } static void -l4_init_bchan_out(struct FsmInst *fi, int event, void *arg) +lli_init_bchan_out(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - SETBIT(chanp->Flags, FLG_LL_DCONN); + test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) - link_debug(chanp, "STAT_DCONN", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_DCONN"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - init_ds(chanp, 0); - SETBIT(chanp->Flags, FLG_START_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); + chanp->cs->iif.statcallb(&ic); + init_b_st(chanp, 0); + test_and_set_bit(FLG_START_B, &chanp->Flags); + chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); } static void -l4_go_active(struct FsmInst *fi, int event, void *arg) +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; - SETBIT(chanp->Flags, FLG_CONNECT_B); + test_and_set_bit(FLG_CONNECT_B, &chanp->Flags); if (chanp->debug & 1) - link_debug(chanp, "STAT_BCONN", 0); - SETBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_BCONN"); + test_and_set_bit(FLG_LL_BCONN, &chanp->Flags); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan); } +/* + * RESUME + */ + /* incomming call */ static void -l4_start_dchan(struct FsmInst *fi, int event, void *arg) +lli_start_dchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_WAIT_D); FsmDelTimer(&chanp->drel_timer, 61); - if (chanp->Flags & FLG_ESTAB_D) { - FsmEvent(fi, EV_DLEST, NULL); - } else { - chanp->Flags = FLG_START_D; - FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL); + if (event == EV_ACCEPTD) + test_and_set_bit(FLG_DO_CONNECT, &chanp->Flags); + else if (event == EV_HANGUP) { + test_and_set_bit(FLG_DO_HANGUP, &chanp->Flags); +#ifdef ALERT_REJECT + test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); +#endif } + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { + FsmEvent(fi, EV_DLEST, NULL); + } else if (!test_and_set_bit(FLG_START_D, &chanp->Flags)) + chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); } static void -l4_deliver_call(struct FsmInst *fi, int event, void *arg) +lli_deliver_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; int ret; - char txt[32]; + chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan); /* * Report incoming calls only once to linklevel, use CallFlags * which is set to 3 with each broadcast message in isdnl1.c * and resetted if a interface answered the STAT_ICALL. */ - if ((chanp->sp) && (chanp->sp->CallFlags == 3)) { + if (1) { /* for only one TEI */ FsmChangeState(fi, ST_IN_WAIT_LL); - SETBIT(chanp->Flags, FLG_ESTAB_D); - SETBIT(chanp->Flags, FLG_CALL_REC); + test_and_set_bit(FLG_CALL_REC, &chanp->Flags); if (chanp->debug & 1) - link_debug(chanp, "STAT_ICALL", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_ICALL"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_ICALL; ic.arg = chanp->chan; /* * No need to return "unknown" for calls without OAD, * cause that's handled in linklevel now (replaced by '0') */ - ic.parm.setup = chanp->para.setup; - ret = chanp->sp->iif.statcallb(&ic); - if (chanp->debug & 1) { - sprintf(txt, "statcallb ret=%d", ret); - link_debug(chanp, txt, 1); - } - if (ret) /* if a interface knows this call, reset the CallFlag - * to avoid a second Call report to the linklevel - */ - chanp->sp->CallFlags &= ~(chanp->chan + 1); + ic.parm.setup = chanp->proc->para.setup; + ret = chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, 1, "statcallb ret=%d", ret); switch (ret) { case 1: /* OK, anybody likes this call */ - FsmChangeState(fi, ST_IN_ALERT_SEND); - SETBIT(chanp->Flags, FLG_CALL_ALERT); - chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL); + FsmDelTimer(&chanp->drel_timer, 61); + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) { + FsmChangeState(fi, ST_IN_ALERT_SEND); + test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + } else { + test_and_set_bit(FLG_DO_ALERT, &chanp->Flags); + FsmChangeState(fi, ST_IN_WAIT_D); + test_and_set_bit(FLG_START_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, + DL_ESTABLISH | REQUEST, NULL); + } break; case 2: /* Rejecting Call */ - RESBIT(chanp->Flags, FLG_CALL_REC); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); break; case 0: /* OK, nobody likes this call */ default: /* statcallb problems */ - chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); + if (test_bit(FLG_ESTAB_D, &chanp->Flags) && + !test_bit(FLG_PTP, &chanp->d_st->l2.flag)) + FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61); break; } } else { - chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); + if (test_bit(FLG_ESTAB_D, &chanp->Flags) && + !test_bit(FLG_PTP, &chanp->d_st->l2.flag)) + FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62); } } static void -l4_send_dconnect(struct FsmInst *fi, int event, void *arg) +lli_establish_d(struct FsmInst *fi, int event, void *arg) +{ + /* This establish the D-channel for pending L3 messages + * without blocking the channel + */ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags); + FsmChangeState(fi, ST_IN_WAIT_D); + test_and_set_bit(FLG_START_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL); +} + +static void +lli_do_action(struct FsmInst *fi, int event, void *arg) +{ + struct Channel *chanp = fi->userdata; + + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + if (chanp->leased) { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags); + FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); + } else if (test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags) && + !test_bit(FLG_DO_HANGUP, &chanp->Flags)) { + FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); + test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); + } else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) { + if (test_bit(FLG_DO_HANGUP, &chanp->Flags)) + FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63); + FsmChangeState(fi, ST_IN_ALERT_SEND); + test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc); + } else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) { + FsmChangeState(fi, ST_WAIT_DRELEASE); + chanp->proc->para.cause = 0x15; /* Call Rejected */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); + } +} + +static void +lli_send_dconnect(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_IN_WAIT_CONN_ACK); - chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc); } static void -l4_init_bchan_in(struct FsmInst *fi, int event, void *arg) +lli_init_bchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_BCONN); - SETBIT(chanp->Flags, FLG_LL_DCONN); + test_and_set_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) - link_debug(chanp, "STAT_DCONN", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_DCONN"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DCONN; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); chanp->l2_active_protocol = chanp->l2_protocol; chanp->incoming = !0; - chanp->lc_b.l2_start = 0; - switch (chanp->l2_active_protocol) { - case (ISDN_PROTO_L2_X75I): - chanp->lc_b.l2_establish = !0; - break; - case (ISDN_PROTO_L2_HDLC): - case (ISDN_PROTO_L2_TRANS): - chanp->lc_b.l2_establish = 0; - break; - default: - printk(KERN_WARNING "r9 unknown protocol\n"); - break; - } - init_ds(chanp, !0); - SETBIT(chanp->Flags, FLG_START_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL); + init_b_st(chanp, !0); + test_and_set_bit(FLG_START_B, &chanp->Flags); + chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL); } -/* Call clearing */ +/* Call suspend */ static void -l4_reject_call(struct FsmInst *fi, int event, void *arg) +lli_suspend(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DRELEASE); - chanp->para.cause = 0x15; /* Call Rejected */ - chanp->is.l4.l4l3(&chanp->is, CC_REJECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); + chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc); } +/* Call clearing */ + static void -l4_cancel_call(struct FsmInst *fi, int event, void *arg) +lli_cancel_call(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) - link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_BHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); - } - chanp->para.cause = 0x10; /* Normal Call Clearing */ - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } static void -l4_shutdown_d(struct FsmInst *fi, int event, void *arg) +lli_shutdown_d(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); FsmDelTimer(&chanp->drel_timer, 62); - RESBIT(chanp->Flags, FLG_ESTAB_D); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + if (test_bit(FLG_PTP, &chanp->d_st->l2.flag)) { + FsmChangeState(fi, ST_NULL); + } else { + if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) { + if (chanp->chan) { + if (chanp->cs->channel[0].fi.state != ST_NULL) + return; + } else { + if (chanp->cs->channel[1].fi.state != ST_NULL) + return; + } + } + FsmChangeState(fi, ST_WAIT_DSHUTDOWN); + test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + } } static void -l4_timeout_d(struct FsmInst *fi, int event, void *arg) +lli_timeout_d(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - if (chanp->Flags & FLG_LL_DCONN) { - if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - } + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp, &ic); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); FsmChangeState(fi, ST_NULL); - chanp->Flags = FLG_ESTAB_D; - FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); + chanp->Flags = 0; + test_and_set_bit(FLG_ESTAB_D, &chanp->Flags); + if (!test_bit(FLG_PTP, &chanp->d_st->l2.flag)) + FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } static void -l4_go_null(struct FsmInst *fi, int event, void *arg) +lli_go_null(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; FsmChangeState(fi, ST_NULL); chanp->Flags = 0; FsmDelTimer(&chanp->drel_timer, 63); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } static void -l4_disconn_bchan(struct FsmInst *fi, int event, void *arg) +lli_disconn_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_BRELEASE); - RESBIT(chanp->Flags, FLG_CONNECT_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } static void -l4_send_d_disc(struct FsmInst *fi, int event, void *arg) +lli_send_d_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - - if (chanp->Flags & (FLG_DISC_REC | FLG_REL_REC)) + if (test_bit(FLG_DISC_REC, &chanp->Flags) || + test_bit(FLG_REL_REC, &chanp->Flags)) return; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) - link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_BHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->leased) { + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L0010"); + chanp->cs->iif.statcallb(&ic); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + FsmChangeState(fi, ST_WAIT_DSHUTDOWN); + test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); + } else { + if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) + chanp->proc->para.cause = 0x15; /* Call Reject */ + else + chanp->proc->para.cause = 0x10; /* Normal Call Clearing */ + chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); } - chanp->para.cause = 0x10; /* Normal Call Clearing */ - chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL); - SETBIT(chanp->Flags, FLG_DISC_SEND); } static void -l4_released_bchan(struct FsmInst *fi, int event, void *arg) +lli_released_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DCOMMAND); chanp->data_open = 0; - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) - link_debug(chanp, "STAT_BHUP", 0); - RESBIT(chanp->Flags, FLG_LL_BCONN); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_BHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + release_b_st(chanp); + test_and_clear_bit(FLG_START_B, &chanp->Flags); } static void -l4_release_bchan(struct FsmInst *fi, int event, void *arg) +lli_release_bchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; chanp->data_open = 0; - SETBIT(chanp->Flags, FLG_DISC_REC); + test_and_set_bit(FLG_DISC_REC, &chanp->Flags); FsmChangeState(fi, ST_WAIT_BREL_DISC); - RESBIT(chanp->Flags, FLG_CONNECT_B); - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags); + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } static void -l4_received_d_rel(struct FsmInst *fi, int event, void *arg) +lli_received_d_rel(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - SETBIT(chanp->Flags, FLG_REL_REC); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + FsmChangeState(fi, ST_NULL); + test_and_set_bit(FLG_REL_REC, &chanp->Flags); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) - link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_BHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); - } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { - if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_FLUSH, NULL); - RESBIT(chanp->Flags, FLG_ESTAB_D); - RESBIT(chanp->Flags, FLG_DISC_SEND); - RESBIT(chanp->Flags, FLG_CALL_REC); - RESBIT(chanp->Flags, FLG_CALL_ALERT); - RESBIT(chanp->Flags, FLG_LL_DCONN); - RESBIT(chanp->Flags, FLG_CALL_SEND); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp, &ic); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + lli_timeout_d(fi, event, arg); } static void -l4_received_d_relcnf(struct FsmInst *fi, int event, void *arg) +lli_received_d_relcnf(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; - FsmChangeState(fi, ST_WAIT_DSHUTDOWN); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + FsmChangeState(fi, ST_NULL); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) - link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_BHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); - } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { - if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - } - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_ESTAB_D); - RESBIT(chanp->Flags, FLG_DISC_SEND); - RESBIT(chanp->Flags, FLG_CALL_REC); - RESBIT(chanp->Flags, FLG_CALL_ALERT); - RESBIT(chanp->Flags, FLG_LL_DCONN); - RESBIT(chanp->Flags, FLG_CALL_SEND); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp, &ic); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags); + test_and_clear_bit(FLG_CALL_REC, &chanp->Flags); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + lli_timeout_d(fi, event, arg); } static void -l4_received_d_disc(struct FsmInst *fi, int event, void *arg) +lli_received_d_disc(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; FsmChangeState(fi, ST_WAIT_D_REL_CNF); - SETBIT(chanp->Flags, FLG_DISC_REC); - if (chanp->Flags & FLG_LL_BCONN) { + test_and_set_bit(FLG_DISC_REC, &chanp->Flags); + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) - link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_BHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); - } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND | FLG_CALL_ALERT)) { - if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - } - RESBIT(chanp->Flags, FLG_LL_DCONN); - RESBIT(chanp->Flags, FLG_CALL_SEND); - RESBIT(chanp->Flags, FLG_CALL_ALERT); - chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL); + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp, &ic); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags); + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); + chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc); } /* processing charge info */ static void -l4_charge_info(struct FsmInst *fi, int event, void *arg) +lli_charge_info(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_CINF; ic.arg = chanp->chan; - sprintf(ic.parm.num, "%d", chanp->para.chargeinfo); - chanp->sp->iif.statcallb(&ic); + sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo); + chanp->cs->iif.statcallb(&ic); } /* error procedures */ static void -l4_no_dchan(struct FsmInst *fi, int event, void *arg) +lli_no_dchan(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; if (chanp->debug & 1) - link_debug(chanp, "STAT_NODCH", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_NODCH"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_NODCH; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); } static void -l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg) +lli_no_dchan_ready(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_DHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); } static void -l4_no_dchan_in(struct FsmInst *fi, int event, void *arg) +lli_no_dchan_in(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; + isdn_ctrl ic; - chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc); chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); } static void -l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg) +lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; - chanp->Flags = 0; FsmChangeState(fi, ST_NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); + test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags); if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_DHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + lli_shutdown_d(fi, event, arg); } static void -l4_setup_err(struct FsmInst *fi, int event, void *arg) +lli_setup_err(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_DCONN) { - if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - } - SETBIT(chanp->Flags, FLG_DISC_SEND); /* DISCONN was sent from L3 */ + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp, &ic); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ } static void -l4_connect_err(struct FsmInst *fi, int event, void *arg) +lli_connect_err(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; FsmChangeState(fi, ST_WAIT_DRELEASE); - if (chanp->Flags & FLG_LL_DCONN) { - if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; - ic.command = ISDN_STAT_DHUP; - ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - } - SETBIT(chanp->Flags, FLG_DISC_SEND); /* DISCONN was sent from L3 */ + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); + if (chanp->debug & 1) + link_debug(chanp, 0, "STAT_DHUP"); + lli_deliver_cause(chanp, &ic); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */ } static void -l4_active_dlrl(struct FsmInst *fi, int event, void *arg) +lli_got_dlrl(struct FsmInst *fi, int event, void *arg) { struct Channel *chanp = fi->userdata; isdn_ctrl ic; chanp->data_open = 0; FsmChangeState(fi, ST_NULL); - if (chanp->Flags & FLG_CONNECT_B) { - chanp->lc_b.l2_establish = 0; /* direct reset in lc_b.lcfi */ - FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL); - RESBIT(chanp->Flags, FLG_CONNECT_B); + if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) { + chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL); } - if (chanp->Flags & FLG_LL_BCONN) { + if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) { if (chanp->debug & 1) - link_debug(chanp, "STAT_BHUP", 0); - ic.driver = chanp->sp->myid; + link_debug(chanp, 0, "STAT_BHUP"); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); - RESBIT(chanp->Flags, FLG_LL_BCONN); + chanp->cs->iif.statcallb(&ic); } - if (chanp->Flags & FLG_START_B) { - release_ds(chanp); - RESBIT(chanp->Flags, FLG_START_B); - } - if (chanp->Flags & FLG_LL_DCONN) { + if (test_and_clear_bit(FLG_START_B, &chanp->Flags)) + release_b_st(chanp); + if (chanp->leased) { + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_CAUSE; + ic.arg = chanp->chan; + sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f); + chanp->cs->iif.statcallb(&ic); + ic.driver = chanp->cs->myid; + ic.command = ISDN_STAT_DHUP; + ic.arg = chanp->chan; + chanp->cs->iif.statcallb(&ic); + chanp->Flags = 0; + } else { + test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags); if (chanp->debug & 1) - link_debug(chanp, "STAT_DHUP", 0); - RESBIT(chanp->Flags, FLG_LL_DCONN); - if (chanp->sp->protocol == ISDN_PTYPE_EURO) { - chanp->para.cause = 0x2f; - chanp->para.loc = 0; + link_debug(chanp, 0, "STAT_DHUP"); + if (chanp->cs->protocol == ISDN_PTYPE_EURO) { + chanp->proc->para.cause = 0x2f; + chanp->proc->para.loc = 0; } else { - chanp->para.cause = 0x70; - chanp->para.loc = 0; + chanp->proc->para.cause = 0x70; + chanp->proc->para.loc = 0; } - l4_deliver_cause(chanp); - ic.driver = chanp->sp->myid; + lli_deliver_cause(chanp, &ic); + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_DHUP; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); + chanp->cs->iif.statcallb(&ic); + chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc); + chanp->Flags = 0; + chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL); } - chanp->Flags = 0; - chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL); - FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL); -} -/* *INDENT-OFF* */ -static struct FsmNode fnlist[] = -{ - {ST_NULL, EV_DIAL, l4_prep_dialout}, - {ST_NULL, EV_SETUP_IND, l4_start_dchan}, - {ST_NULL, EV_SHUTDOWN_D, l4_shutdown_d}, - {ST_NULL, EV_DLRL, l4_go_null}, - {ST_OUT_WAIT_D, EV_DLEST, l4_do_dialout}, - {ST_OUT_WAIT_D, EV_DLRL, l4_no_dchan}, - {ST_OUT_WAIT_D, EV_HANGUP, l4_no_dchan}, - {ST_IN_WAIT_D, EV_DLEST, l4_deliver_call}, - {ST_IN_WAIT_D, EV_DLRL, l4_no_dchan_in}, - {ST_IN_WAIT_D, EV_HANGUP, l4_no_dchan_in}, - {ST_OUT_DIAL, EV_SETUP_CNF, l4_init_bchan_out}, - {ST_OUT_DIAL, EV_HANGUP, l4_cancel_call}, - {ST_OUT_DIAL, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_OUT_DIAL, EV_RELEASE_IND, l4_received_d_rel}, - {ST_OUT_DIAL, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_OUT_DIAL, EV_NOSETUP_RSP, l4_no_setup_rsp}, - {ST_OUT_DIAL, EV_SETUP_ERR, l4_setup_err}, - {ST_IN_WAIT_LL, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_WAIT_LL, EV_ACCEPTD, l4_send_dconnect}, - {ST_IN_WAIT_LL, EV_HANGUP, l4_reject_call}, - {ST_IN_WAIT_LL, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_WAIT_LL, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_WAIT_LL, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_ALERT_SEND, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_ALERT_SEND, EV_ACCEPTD, l4_send_dconnect}, - {ST_IN_ALERT_SEND, EV_HANGUP, l4_send_d_disc}, - {ST_IN_ALERT_SEND, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_ALERT_SEND, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_ALERT_SEND, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, l4_init_bchan_in}, - {ST_IN_WAIT_CONN_ACK, EV_HANGUP, l4_send_d_disc}, - {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_IN_WAIT_CONN_ACK, EV_RELEASE_IND, l4_received_d_rel}, - {ST_IN_WAIT_CONN_ACK, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, l4_connect_err}, - {ST_WAIT_BCONN, EV_BC_EST, l4_go_active}, - {ST_WAIT_BCONN, EV_BC_REL, l4_send_d_disc}, - {ST_WAIT_BCONN, EV_HANGUP, l4_send_d_disc}, - {ST_WAIT_BCONN, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_BCONN, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_BCONN, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_ACTIVE, EV_CINF, l4_charge_info}, - {ST_ACTIVE, EV_BC_REL, l4_released_bchan}, - {ST_ACTIVE, EV_HANGUP, l4_disconn_bchan}, - {ST_ACTIVE, EV_DISCONNECT_IND, l4_release_bchan}, - {ST_ACTIVE, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_ACTIVE, EV_RELEASE_IND, l4_received_d_rel}, - {ST_ACTIVE, EV_DLRL, l4_active_dlrl}, - {ST_WAIT_BRELEASE, EV_BC_REL, l4_send_d_disc}, - {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_BRELEASE, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_BRELEASE, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_BREL_DISC, EV_BC_REL, l4_received_d_disc}, - {ST_WAIT_BREL_DISC, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_BREL_DISC, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_DCOMMAND, EV_HANGUP, l4_send_d_disc}, - {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, l4_received_d_disc}, - {ST_WAIT_DCOMMAND, EV_RELEASE_CNF, l4_received_d_relcnf}, - {ST_WAIT_DCOMMAND, EV_RELEASE_IND, l4_received_d_rel}, - {ST_WAIT_DRELEASE, EV_RELEASE_IND, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_RELEASE_CNF, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_RELEASE_ERR, l4_timeout_d}, - {ST_WAIT_DRELEASE, EV_DIAL, l4_no_dchan_ready}, - {ST_WAIT_D_REL_CNF, EV_RELEASE_CNF, l4_timeout_d}, - {ST_WAIT_D_REL_CNF, EV_RELEASE_ERR, l4_timeout_d}, - {ST_WAIT_D_REL_CNF, EV_DIAL, l4_no_dchan_ready}, - {ST_WAIT_DSHUTDOWN, EV_DLRL, l4_go_null}, - {ST_WAIT_DSHUTDOWN, EV_DIAL, l4_prep_dialout}, - {ST_WAIT_DSHUTDOWN, EV_SETUP_IND, l4_start_dchan}, -}; -/* *INDENT-ON* */ - - - - -#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) - -static void -lc_r1(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_ACTIVATE_WAIT); - FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50); - lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL); - -} - -static void -lc_r6(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmDelTimer(&lf->act_timer, 50); - FsmChangeState(fi, ST_LC_DELAY); - FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51); -} - -static void -lc_r2(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - if (lf->l2_establish) { - FsmChangeState(fi, ST_LC_ESTABLISH_WAIT); - if (lf->l2_start) - lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL); - } else { - FsmChangeState(fi, ST_LC_CONNECTED); - lf->lccall(lf, LC_ESTABLISH, NULL); - } -} - -static void -lc_r3(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_CONNECTED); - lf->lccall(lf, LC_ESTABLISH, NULL); -} - -static void -lc_r7(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_FLUSH_WAIT); - lf->st->ma.manl2(lf->st, DL_FLUSH, NULL); -} - -static void -lc_r4(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - if (lf->l2_establish) { - FsmChangeState(fi, ST_LC_RELEASE_WAIT); - lf->st->ma.manl2(lf->st, DL_RELEASE, NULL); - /* This timer is for releasing the channel even - * there is a hang in layer 2 ; 5 sec are a try - */ - FsmAddTimer(&lf->act_timer, 5000, EV_LC_TIMER, NULL, 53); - } else { - FsmChangeState(fi, ST_LC_NULL); - lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL); - lf->lccall(lf, LC_RELEASE, NULL); - } -} - -static void -lc_r4_1(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_FLUSH_DELAY); - FsmAddTimer(&lf->act_timer, 50, EV_LC_TIMER, NULL, 52); -} - -static void -lc_r5_1(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmChangeState(fi, ST_LC_RELEASE_WAIT); - /* This delay is needed for send out the UA frame before - * PH_DEACTIVATE the interface - */ - FsmAddTimer(&lf->act_timer, 10, EV_LC_TIMER, NULL, 54); + chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan); } -static void -lc_r5(struct FsmInst *fi, int event, void *arg) -{ - struct LcFsm *lf = fi->userdata; - - FsmDelTimer(&lf->act_timer, 54); - FsmChangeState(fi, ST_LC_NULL); - lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL); - lf->lccall(lf, LC_RELEASE, NULL); -} /* *INDENT-OFF* */ -static struct FsmNode LcFnList[] = +static struct FsmNode fnlist[] HISAX_INITDATA = { - {ST_LC_NULL, EV_LC_ESTABLISH, lc_r1}, - {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_r6}, - {ST_LC_DELAY, EV_LC_TIMER, lc_r2}, - {ST_LC_DELAY, EV_LC_DL_ESTABLISH, lc_r3}, - {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_r3}, - {ST_LC_ESTABLISH_WAIT, EV_LC_RELEASE, lc_r5}, - {ST_LC_CONNECTED, EV_LC_FLUSH, lc_r7}, - {ST_LC_CONNECTED, EV_LC_RELEASE, lc_r4}, - {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_r5_1}, - {ST_LC_FLUSH_WAIT, EV_LC_DL_FLUSH, lc_r4_1}, - {ST_LC_FLUSH_DELAY, EV_LC_TIMER, lc_r4}, - {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_r5}, - {ST_LC_RELEASE_WAIT, EV_LC_TIMER, lc_r5}, - {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_r5}, - {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_r5}, + {ST_NULL, EV_DIAL, lli_prep_dialout}, + {ST_NULL, EV_RESUME, lli_prep_dialout}, + {ST_NULL, EV_SETUP_IND, lli_deliver_call}, + {ST_NULL, EV_SHUTDOWN_D, lli_shutdown_d}, + {ST_NULL, EV_DLRL, lli_go_null}, + {ST_NULL, EV_DLEST, lli_d_established}, + {ST_NULL, EV_ESTABLISH, lli_establish_d}, + {ST_OUT_WAIT_D, EV_DLEST, lli_do_dialout}, + {ST_OUT_WAIT_D, EV_DLRL, lli_no_dchan}, + {ST_OUT_WAIT_D, EV_HANGUP, lli_no_dchan}, + {ST_IN_WAIT_D, EV_DLEST, lli_do_action}, + {ST_IN_WAIT_D, EV_DLRL, lli_no_dchan_in}, + {ST_IN_WAIT_D, EV_ACCEPTD, lli_start_dchan}, + {ST_IN_WAIT_D, EV_HANGUP, lli_start_dchan}, + {ST_OUT_DIAL, EV_SETUP_CNF, lli_init_bchan_out}, + {ST_OUT_DIAL, EV_HANGUP, lli_cancel_call}, + {ST_OUT_DIAL, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_OUT_DIAL, EV_RELEASE_IND, lli_received_d_rel}, + {ST_OUT_DIAL, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_OUT_DIAL, EV_NOSETUP_RSP, lli_no_setup_rsp}, + {ST_OUT_DIAL, EV_SETUP_ERR, lli_setup_err}, + {ST_OUT_DIAL, EV_DLRL, lli_got_dlrl}, + {ST_IN_WAIT_LL, EV_DLEST, lli_d_established}, + {ST_IN_WAIT_LL, EV_DLRL, lli_d_released}, + {ST_IN_WAIT_LL, EV_ACCEPTD, lli_start_dchan}, + {ST_IN_WAIT_LL, EV_HANGUP, lli_start_dchan}, + {ST_IN_WAIT_LL, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_WAIT_LL, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_WAIT_LL, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_ALERT_SEND, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_ALERT_SEND, EV_ACCEPTD, lli_send_dconnect}, + {ST_IN_ALERT_SEND, EV_HANGUP, lli_send_d_disc}, + {ST_IN_ALERT_SEND, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_ALERT_SEND, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_ALERT_SEND, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_ALERT_SEND, EV_DLRL, lli_got_dlrl}, + {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, lli_init_bchan_in}, + {ST_IN_WAIT_CONN_ACK, EV_HANGUP, lli_send_d_disc}, + {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE_IND, lli_received_d_rel}, + {ST_IN_WAIT_CONN_ACK, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, lli_connect_err}, + {ST_IN_WAIT_CONN_ACK, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BCONN, EV_BC_EST, lli_go_active}, + {ST_WAIT_BCONN, EV_BC_REL, lli_send_d_disc}, + {ST_WAIT_BCONN, EV_HANGUP, lli_send_d_disc}, + {ST_WAIT_BCONN, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_BCONN, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BCONN, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BCONN, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BCONN, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_CINF, lli_charge_info}, + {ST_ACTIVE, EV_BC_REL, lli_released_bchan}, + {ST_ACTIVE, EV_SUSPEND, lli_suspend}, + {ST_ACTIVE, EV_HANGUP, lli_disconn_bchan}, + {ST_ACTIVE, EV_DISCONNECT_IND, lli_release_bchan}, + {ST_ACTIVE, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_ACTIVE, EV_RELEASE_IND, lli_received_d_rel}, + {ST_ACTIVE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BRELEASE, EV_BC_REL, lli_send_d_disc}, + {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_BRELEASE, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BRELEASE, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BRELEASE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_BREL_DISC, EV_BC_REL, lli_received_d_disc}, + {ST_WAIT_BREL_DISC, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_BREL_DISC, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_BREL_DISC, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DCOMMAND, EV_HANGUP, lli_send_d_disc}, + {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, lli_received_d_disc}, + {ST_WAIT_DCOMMAND, EV_RELEASE_CNF, lli_received_d_relcnf}, + {ST_WAIT_DCOMMAND, EV_RELEASE_IND, lli_received_d_rel}, + {ST_WAIT_DCOMMAND, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DRELEASE, EV_RELEASE_IND, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_RELEASE_CNF, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_RELEASE_ERR, lli_timeout_d}, + {ST_WAIT_DRELEASE, EV_DIAL, lli_no_dchan_ready}, + {ST_WAIT_DRELEASE, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_D_REL_CNF, EV_RELEASE_CNF, lli_timeout_d}, + {ST_WAIT_D_REL_CNF, EV_RELEASE_ERR, lli_timeout_d}, +/* ETS 300-104 16.1 */ + {ST_WAIT_D_REL_CNF, EV_RELEASE_IND, lli_timeout_d}, + {ST_WAIT_D_REL_CNF, EV_DIAL, lli_no_dchan_ready}, + {ST_WAIT_D_REL_CNF, EV_DLRL, lli_got_dlrl}, + {ST_WAIT_DSHUTDOWN, EV_DLRL, lli_go_null}, + {ST_WAIT_DSHUTDOWN, EV_DLEST, lli_d_established}, + {ST_WAIT_DSHUTDOWN, EV_DIAL, lli_prep_dialout}, + {ST_WAIT_DSHUTDOWN, EV_RESUME, lli_prep_dialout}, + {ST_WAIT_DSHUTDOWN, EV_SETUP_IND, lli_deliver_call}, }; /* *INDENT-ON* */ +#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode)) - - - - - - - -#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode)) - -void -CallcNew(void) +HISAX_INITFUNC(void +CallcNew(void)) { callcfsm.state_count = STATE_COUNT; callcfsm.event_count = EVENT_COUNT; callcfsm.strEvent = strEvent; callcfsm.strState = strState; FsmNew(&callcfsm, fnlist, FNCOUNT); - - lcfsm.state_count = LC_STATE_COUNT; - lcfsm.event_count = LC_EVENT_COUNT; - lcfsm.strEvent = strLcEvent; - lcfsm.strState = strLcState; - FsmNew(&lcfsm, LcFnList, LC_FN_COUNT); } void CallcFree(void) { - FsmFree(&lcfsm); FsmFree(&callcfsm); } static void -release_ds(struct Channel *chanp) +release_b_st(struct Channel *chanp) { - struct PStack *st = &chanp->ds; - struct IsdnCardState *sp; - struct HscxState *hsp; - - sp = st->l1.hardware; - hsp = sp->hs + chanp->hscx; - - close_hscxstate(hsp); + struct PStack *st = chanp->b_st; + chanp->bcs->BC_Close(chanp->bcs); switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): releasestack_isdnl2(st); break; case (ISDN_PROTO_L2_HDLC): case (ISDN_PROTO_L2_TRANS): +// case (ISDN_PROTO_L2_MODEM): releasestack_transl2(st); break; } - /* Reset B-Channel Statemachine */ - FsmDelTimer(&chanp->lc_b.act_timer, 79); - FsmChangeState(&chanp->lc_b.lcfi, ST_LC_NULL); } -static void -cc_l1man(struct PStack *st, int pr, void *arg) +struct Channel +*selectfreechannel(struct PStack *st) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp = st->lli.userdata; + int i; - switch (pr) { - case (PH_ACTIVATE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL); - break; - case (PH_DEACTIVATE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL); - break; + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i=1; + else + i=0; + while (i<2) { + if (chanp->fi.state == ST_NULL) + return (chanp); + chanp++; + i++; } + return (NULL); } -static void -cc_l2man(struct PStack *st, int pr, void *arg) +int +is_activ(struct PStack *st) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp = st->lli.userdata; + int i; - switch (pr) { - case (DL_ESTABLISH): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL); - break; - case (DL_RELEASE): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL); - break; - case (DL_FLUSH): - FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_FLUSH, NULL); - break; + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i=1; + else + i=0; + while (i<2) { + if (test_bit(FLG_ESTAB_D, &chanp->Flags)) + return (1); + chanp++; + i++; } + return (0); } static void -dcc_l1man(struct PStack *st, int pr, void *arg) +dchan_l3l4(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct l3_process *pc = arg; + struct IsdnCardState *cs = st->l1.hardware; + struct Channel *chanp; + int event; switch (pr) { - case (PH_ACTIVATE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL); - break; - case (PH_DEACTIVATE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL); + case (DL_ESTABLISH | INDICATION): + event = EV_DLEST; break; - } -} - -static void -dcc_l2man(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - - switch (pr) { - case (DL_ESTABLISH): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL); + case (DL_RELEASE | INDICATION): + event = EV_DLRL; break; - case (DL_RELEASE): - FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL); + default: + event = -1; break; } -} - -static void -l2tei_dummy(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - char tmp[64], tm[32]; - - jiftime(tm, jiffies); - sprintf(tmp, "%s Channel %d Warning! Dummy l2tei called pr=%d\n", tm, chanp->chan, pr); - HiSax_putstatus(chanp->sp, tmp); -} + if (event >= 0) { + int i; -static void -ll_handler(struct PStack *st, int pr, void *arg) -{ - struct Channel *chanp = (struct Channel *) st->l4.userdata; - char tmp[64], tm[32]; + chanp = st->lli.userdata; + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + i = 1; + else + i = 0; + while (i < 2) { + FsmEvent(&chanp->fi, event, NULL); + chanp++; + i++; + } + return; + } else if (pr == (CC_SETUP | INDICATION)) { + if (!(chanp = selectfreechannel(pc->st))) { + pc->st->lli.l4l3(pc->st, CC_DLRL | REQUEST, pc); + } else { + chanp->proc = pc; + pc->chan = chanp; + FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); + } + return; + } + if (!(chanp = pc->chan)) + return; switch (pr) { - case (CC_DISCONNECT_IND): + case (CC_DISCONNECT | INDICATION): FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL); break; - case (CC_RELEASE_CNF): + case (CC_RELEASE | CONFIRM): FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); break; - case (CC_SETUP_IND): - FsmEvent(&chanp->fi, EV_SETUP_IND, NULL); + case (CC_SUSPEND | CONFIRM): + FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + break; + case (CC_RESUME | CONFIRM): + FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); break; - case (CC_RELEASE_IND): + case (CC_RESUME_ERR): + FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL); + break; + case (CC_RELEASE | INDICATION): FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL); break; - case (CC_SETUP_COMPLETE_IND): + case (CC_SETUP_COMPL | INDICATION): FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL); break; - case (CC_SETUP_CNF): + case (CC_SETUP | CONFIRM): FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL); break; - case (CC_INFO_CHARGE): + case (CC_CHARGE | INDICATION): FsmEvent(&chanp->fi, EV_CINF, NULL); break; - case (CC_NOSETUP_RSP_ERR): + case (CC_NOSETUP_RSP): FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL); break; case (CC_SETUP_ERR): @@ -1386,130 +1307,97 @@ case (CC_RELEASE_ERR): FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL); break; + case (CC_PROCEEDING | INDICATION): + case (CC_ALERTING | INDICATION): + break; default: - if (chanp->debug & 2048) { - jiftime(tm, jiffies); - sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n", - tm, chanp->chan, pr); - HiSax_putstatus(chanp->sp, tmp); + if (chanp->debug & 0x800) { + HiSax_putstatus(chanp->cs, "Ch", + "%d L3->L4 unknown primitiv %x", + chanp->chan, pr); } } } static void -init_is(struct Channel *chanp, unsigned int ces) +init_d_st(struct Channel *chanp) { - struct PStack *st = &chanp->is; - struct IsdnCardState *sp = chanp->sp; - char tmp[128]; + struct PStack *st = chanp->d_st; + struct IsdnCardState *cs = chanp->cs; + char tmp[16]; - setstack_HiSax(st, sp); + HiSax_addlist(cs, st); + setstack_HiSax(st, cs); st->l2.sap = 0; - st->l2.tei = 255; - st->l2.ces = ces; - st->l2.extended = !0; - st->l2.laptype = LAPD; + st->l2.tei = -1; + st->l2.flag = 0; + test_and_set_bit(FLG_MOD128, &st->l2.flag); + test_and_set_bit(FLG_LAPD, &st->l2.flag); + test_and_set_bit(FLG_ORIG, &st->l2.flag); + st->l2.maxlen = MAX_DFRAME_LEN; st->l2.window = 1; - st->l2.orig = !0; - st->l2.t200 = 1000; /* 1000 milliseconds */ - if (st->protocol == ISDN_PTYPE_1TR6) { - st->l2.n200 = 3; /* try 3 times */ - st->l2.t203 = 10000; /* 10000 milliseconds */ - } else { - st->l2.n200 = 4; /* try 4 times */ - st->l2.t203 = 5000; /* 5000 milliseconds */ - } - sprintf(tmp, "Channel %d q.921", chanp->chan); + st->l2.T200 = 1000; /* 1000 milliseconds */ + st->l2.N200 = 3; /* try 3 times */ + st->l2.T203 = 10000; /* 10000 milliseconds */ + if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) + sprintf(tmp, "DCh%d Q.921 ", chanp->chan); + else + sprintf(tmp, "DCh Q.921 "); setstack_isdnl2(st, tmp); - setstack_isdnl3(st, chanp); - st->l4.userdata = chanp; - st->l4.l2writewakeup = NULL; - st->l3.l3l4 = ll_handler; - st->l1.l1man = cc_l1man; - st->l2.l2man = cc_l2man; - st->pa = &chanp->para; - HiSax_addlist(sp, st); + setstack_l3dc(st, chanp); + st->lli.userdata = chanp; + st->lli.l2writewakeup = NULL; + st->l3.l3l4 = dchan_l3l4; } static void -callc_debug(struct FsmInst *fi, char *s) +callc_debug(struct FsmInst *fi, char *fmt, ...) { - char str[80], tm[32]; + va_list args; struct Channel *chanp = fi->userdata; + char tmp[16]; - jiftime(tm, jiffies); - sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s); - HiSax_putstatus(chanp->sp, str); + va_start(args, fmt); + sprintf(tmp, "Ch%d callc ", chanp->chan); + VHiSax_putstatus(chanp->cs, tmp, fmt, args); + va_end(args); } static void -lc_debug(struct FsmInst *fi, char *s) -{ - char str[256], tm[32]; - struct LcFsm *lf = fi->userdata; - - jiftime(tm, jiffies); - sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s); - HiSax_putstatus(lf->ch->sp, str); +dummy_pstack(struct PStack *st, int pr, void *arg) { + printk(KERN_WARNING"call to dummy_pstack pr=%04x arg %lx\n", pr, (long)arg); } static void -dlc_debug(struct FsmInst *fi, char *s) -{ - char str[256], tm[32]; - struct LcFsm *lf = fi->userdata; - - jiftime(tm, jiffies); - sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s); - HiSax_putstatus(lf->ch->sp, str); +init_PStack(struct PStack **stp) { + *stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC); + (*stp)->next = NULL; + (*stp)->l1.l1l2 = dummy_pstack; + (*stp)->l1.l1hw = dummy_pstack; + (*stp)->l1.l1tei = dummy_pstack; + (*stp)->l2.l2tei = dummy_pstack; + (*stp)->l2.l2l1 = dummy_pstack; + (*stp)->l2.l2l3 = dummy_pstack; + (*stp)->l3.l3l2 = dummy_pstack; + (*stp)->l3.l3l4 = dummy_pstack; + (*stp)->lli.l4l3 = dummy_pstack; + (*stp)->ma.layer = dummy_pstack; } static void -lccall_d(struct LcFsm *lf, int pr, void *arg) -{ - struct Channel *chanp = lf->ch; - - switch (pr) { - case (LC_ESTABLISH): - FsmEvent(&chanp->fi, EV_DLEST, NULL); - break; - case (LC_RELEASE): - FsmEvent(&chanp->fi, EV_DLRL, NULL); - break; - } -} - -static void -lccall_b(struct LcFsm *lf, int pr, void *arg) -{ - struct Channel *chanp = lf->ch; - - switch (pr) { - case (LC_ESTABLISH): - FsmEvent(&chanp->fi, EV_BC_EST, NULL); - break; - case (LC_RELEASE): - FsmEvent(&chanp->fi, EV_BC_REL, NULL); - break; - } -} - -static void -init_chan(int chan, struct IsdnCardState *csta, int hscx, - unsigned int ces) +init_chan(int chan, struct IsdnCardState *csta) { struct Channel *chanp = csta->channel + chan; - chanp->sp = csta; - chanp->hscx = hscx; + chanp->cs = csta; + chanp->bcs = csta->bcs + chan; chanp->chan = chan; chanp->incoming = 0; chanp->debug = 0; chanp->Flags = 0; chanp->leased = 0; - chanp->impair = 0; - init_is(chanp, ces); - + init_PStack(&chanp->b_st); + chanp->b_st->l1.delay = DEFAULT_B_DELAY; chanp->fi.fsm = &callcfsm; chanp->fi.state = ST_NULL; chanp->fi.debug = 0; @@ -1517,58 +1405,46 @@ chanp->fi.printdebug = callc_debug; FsmInitTimer(&chanp->fi, &chanp->dial_timer); FsmInitTimer(&chanp->fi, &chanp->drel_timer); - - chanp->lc_d.lcfi.fsm = &lcfsm; - chanp->lc_d.lcfi.state = ST_LC_NULL; - chanp->lc_d.lcfi.debug = 0; - chanp->lc_d.lcfi.userdata = &chanp->lc_d; - chanp->lc_d.lcfi.printdebug = lc_debug; - chanp->lc_d.type = LC_D; - chanp->lc_d.ch = chanp; - chanp->lc_d.st = &chanp->is; - chanp->lc_d.l2_establish = !0; - chanp->lc_d.l2_start = !0; - chanp->lc_d.lccall = lccall_d; - FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer); - - chanp->lc_b.lcfi.fsm = &lcfsm; - chanp->lc_b.lcfi.state = ST_LC_NULL; - chanp->lc_b.lcfi.debug = 0; - chanp->lc_b.lcfi.userdata = &chanp->lc_b; - chanp->lc_b.lcfi.printdebug = dlc_debug; - chanp->lc_b.type = LC_B; - chanp->lc_b.ch = chanp; - chanp->lc_b.st = &chanp->ds; - chanp->lc_b.l2_establish = !0; - chanp->lc_b.l2_start = !0; - chanp->lc_b.lccall = lccall_b; - FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer); - chanp->outcallref = 64; + if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + init_PStack(&chanp->d_st); + if (chan) + csta->channel->d_st->next = chanp->d_st; + chanp->d_st->next = NULL; + init_d_st(chanp); + } else { + chanp->d_st = csta->channel->d_st; + } chanp->data_open = 0; } int CallcNewChan(struct IsdnCardState *csta) { - int ces; - chancount += 2; - ces = randomces(); - init_chan(0, csta, 1, ces++); - ces %= 0xffff; - init_chan(1, csta, 0, ces++); + init_chan(0, csta); + init_chan(1, csta); printk(KERN_INFO "HiSax: 2 channels added\n"); + if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) { + printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); + test_and_set_bit(FLG_START_D, &csta->channel->Flags); + csta->channel->d_st->lli.l4l3(csta->channel->d_st, + DL_ESTABLISH | REQUEST, NULL); + } return (2); } static void -release_is(struct Channel *chanp) +release_d_st(struct Channel *chanp) { - struct PStack *st = &chanp->is; + struct PStack *st = chanp->d_st; + if (!st) + return; releasestack_isdnl2(st); releasestack_isdnl3(st); HiSax_rmlist(st->l1.hardware, st); + kfree(st); + chanp->d_st = NULL; } void @@ -1579,32 +1455,46 @@ for (i = 0; i < 2; i++) { FsmDelTimer(&csta->channel[i].drel_timer, 74); FsmDelTimer(&csta->channel[i].dial_timer, 75); - FsmDelTimer(&csta->channel[i].lc_b.act_timer, 76); - FsmDelTimer(&csta->channel[i].lc_d.act_timer, 77); - if (csta->channel[i].Flags & FLG_START_B) { - release_ds(csta->channel + i); - } - release_is(csta->channel + i); + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) + release_d_st(csta->channel + i); + if (csta->channel[i].b_st) { + if (test_and_clear_bit(FLG_START_B, &csta->channel[i].Flags)) + release_b_st(csta->channel + i); + kfree(csta->channel[i].b_st); + csta->channel[i].b_st = NULL; + } else + printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i); + if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + release_d_st(csta->channel + i); + } else + csta->channel[i].d_st = NULL; } } static void lldata_handler(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; struct sk_buff *skb = arg; switch (pr) { - case (DL_DATA): + case (DL_DATA | INDICATION): if (chanp->data_open) - chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); } break; + case (DL_ESTABLISH | INDICATION): + case (DL_ESTABLISH | CONFIRM): + FsmEvent(&chanp->fi, EV_BC_EST, NULL); + break; + case (DL_RELEASE | INDICATION): + case (DL_RELEASE | CONFIRM): + FsmEvent(&chanp->fi, EV_BC_REL, NULL); + break; default: - printk(KERN_WARNING "lldata_handler unknown primitive %d\n", + printk(KERN_WARNING "lldata_handler unknown primitive %x\n", pr); break; } @@ -1613,99 +1503,167 @@ static void lltrans_handler(struct PStack *st, int pr, void *arg) { - struct Channel *chanp = (struct Channel *) st->l4.userdata; + struct Channel *chanp = (struct Channel *) st->lli.userdata; struct sk_buff *skb = arg; switch (pr) { - case (PH_DATA): + case (PH_DATA | INDICATION): if (chanp->data_open) - chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb); + chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb); else { - SET_SKB_FREE(skb); + link_debug(chanp, 0, "channel not open"); dev_kfree_skb(skb, FREE_READ); } break; + case (PH_ACTIVATE | INDICATION): + case (PH_ACTIVATE | CONFIRM): + FsmEvent(&chanp->fi, EV_BC_EST, NULL); + break; + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + FsmEvent(&chanp->fi, EV_BC_REL, NULL); + break; default: - printk(KERN_WARNING "lltrans_handler unknown primitive %d\n", + printk(KERN_WARNING "lltrans_handler unknown primitive %x\n", pr); break; } } static void -ll_writewakeup(struct PStack *st) +ll_writewakeup(struct PStack *st, int len) { - struct Channel *chanp = st->l4.userdata; + struct Channel *chanp = st->lli.userdata; isdn_ctrl ic; - ic.driver = chanp->sp->myid; + ic.driver = chanp->cs->myid; ic.command = ISDN_STAT_BSENT; ic.arg = chanp->chan; - chanp->sp->iif.statcallb(&ic); +// ic.parm.length = len; + chanp->cs->iif.statcallb(&ic); } static int -init_ds(struct Channel *chanp, int incoming) +init_b_st(struct Channel *chanp, int incoming) { - struct PStack *st = &chanp->ds; - struct IsdnCardState *sp = chanp->sp; - struct HscxState *hsp = sp->hs + chanp->hscx; - char tmp[128]; - - st->l1.hardware = sp; - - hsp->mode = 2; - - if (setstack_hscx(st, hsp)) + struct PStack *st = chanp->b_st; + struct IsdnCardState *cs = chanp->cs; + char tmp[16]; + + st->l1.hardware = cs; + if (chanp->leased) + st->l1.bc = chanp->chan & 1; + else + st->l1.bc = chanp->proc->para.bchannel - 1; + switch (chanp->l2_active_protocol) { + case (ISDN_PROTO_L2_X75I): + case (ISDN_PROTO_L2_HDLC): + st->l1.mode = L1_MODE_HDLC; + break; + case (ISDN_PROTO_L2_TRANS): + st->l1.mode = L1_MODE_TRANS; + break; +#if 0 + case (ISDN_PROTO_L2_MODEM): + st->l1.mode = L1_MODE_MODEM; + break; +#endif + } + if (chanp->bcs->BC_SetStack(st, chanp->bcs)) return (-1); - - st->l2.extended = 0; - st->l2.laptype = LAPB; - st->l2.orig = !incoming; - st->l2.t200 = 1000; /* 1000 milliseconds */ + st->l2.flag = 0; + test_and_set_bit(FLG_LAPB, &st->l2.flag); + st->l2.maxlen = MAX_DATA_SIZE; + if (!incoming) + test_and_set_bit(FLG_ORIG, &st->l2.flag); + st->l2.T200 = 1000; /* 1000 milliseconds */ st->l2.window = 7; - st->l2.n200 = 4; /* try 4 times */ - st->l2.t203 = 5000; /* 5000 milliseconds */ - + st->l2.N200 = 4; /* try 4 times */ + st->l2.T203 = 5000; /* 5000 milliseconds */ st->l3.debug = 0; switch (chanp->l2_active_protocol) { case (ISDN_PROTO_L2_X75I): - sprintf(tmp, "Channel %d x.75", chanp->chan); + sprintf(tmp, "Ch%d X.75", chanp->chan); setstack_isdnl2(st, tmp); + setstack_l3bc(st, chanp); st->l2.l2l3 = lldata_handler; - st->l1.l1man = dcc_l1man; - st->l2.l2man = dcc_l2man; - st->l2.l2tei = l2tei_dummy; - st->l4.userdata = chanp; - st->l4.l1writewakeup = NULL; - st->l4.l2writewakeup = ll_writewakeup; + st->lli.userdata = chanp; + st->lli.l1writewakeup = NULL; + st->lli.l2writewakeup = ll_writewakeup; st->l2.l2m.debug = chanp->debug & 16; st->l2.debug = chanp->debug & 64; - st->ma.manl2(st, MDL_NOTEIPROC, NULL); - st->l1.hscxmode = 2; /* Packet-Mode ? */ - st->l1.hscxchannel = chanp->para.bchannel - 1; break; case (ISDN_PROTO_L2_HDLC): - st->l1.l1l2 = lltrans_handler; - st->l1.l1man = dcc_l1man; - st->l4.userdata = chanp; - st->l4.l1writewakeup = ll_writewakeup; - st->l1.hscxmode = 2; - st->l1.hscxchannel = chanp->para.bchannel - 1; - break; case (ISDN_PROTO_L2_TRANS): +// case (ISDN_PROTO_L2_MODEM): st->l1.l1l2 = lltrans_handler; - st->l1.l1man = dcc_l1man; - st->l4.userdata = chanp; - st->l4.l1writewakeup = ll_writewakeup; - st->l1.hscxmode = 1; - st->l1.hscxchannel = chanp->para.bchannel - 1; + st->lli.userdata = chanp; + st->lli.l1writewakeup = ll_writewakeup; + setstack_transl2(st); + setstack_l3bc(st, chanp); break; } return (0); } static void +leased_l4l3(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->lli.userdata; + struct sk_buff *skb = arg; + + switch (pr) { + case (DL_DATA | REQUEST): + link_debug(chanp, 0, "leased line d-channel DATA"); + dev_kfree_skb(skb, FREE_READ); + break; + case (DL_ESTABLISH | REQUEST): + st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL); + break; + case (DL_RELEASE | REQUEST): + break; + default: + printk(KERN_WARNING "transd_l4l3 unknown primitive %x\n", + pr); + break; + } +} + +static void +leased_l1l2(struct PStack *st, int pr, void *arg) +{ + struct Channel *chanp = (struct Channel *) st->lli.userdata; + struct sk_buff *skb = arg; + int i,event = EV_DLRL; + + switch (pr) { + case (PH_DATA | INDICATION): + link_debug(chanp, 0, "leased line d-channel DATA"); + dev_kfree_skb(skb, FREE_READ); + break; + case (PH_ACTIVATE | INDICATION): + case (PH_ACTIVATE | CONFIRM): + event = EV_DLEST; + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) + i = 1; + else + i = 0; + while (i < 2) { + FsmEvent(&chanp->fi, event, NULL); + chanp++; + i++; + } + break; + default: + printk(KERN_WARNING + "transd_l1l2 unknown primitive %x\n", pr); + break; + } +} + +static void channel_report(struct Channel *chanp) { } @@ -1719,25 +1677,86 @@ for (i = 0; i < 2; i++) { chanp[i].debug = debugflags; chanp[i].fi.debug = debugflags & 2; - chanp[i].is.l2.l2m.debug = debugflags & 8; - chanp[i].ds.l2.l2m.debug = debugflags & 16; - chanp[i].is.l2.debug = debugflags & 32; - chanp[i].ds.l2.debug = debugflags & 64; - chanp[i].lc_d.lcfi.debug = debugflags & 128; - chanp[i].lc_b.lcfi.debug = debugflags & 256; + chanp[i].d_st->l2.l2m.debug = debugflags & 8; + chanp[i].b_st->l2.l2m.debug = debugflags & 0x10; + chanp[i].d_st->l2.debug = debugflags & 0x20; + chanp[i].b_st->l2.debug = debugflags & 0x40; + chanp[i].d_st->l3.l3m.debug = debugflags & 0x80; + chanp[i].b_st->l3.l3m.debug = debugflags & 0x100; + chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200; + chanp[i].b_st->ma.debug = debugflags & 0x200; + chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000; + chanp[i].b_st->l1.l1m.debug = debugflags & 0x2000; } - csta->dlogflag = debugflags & 4; - csta->teistack->l2.l2m.debug = debugflags & 512; + if (debugflags & 4) + csta->debug |= DEB_DLOG_HEX; + else + csta->debug &= ~DEB_DLOG_HEX; } +#if 0 +static char tmpbuf[256]; + +static void +capi_debug(struct Channel *chanp, capi_msg *cm) +{ + char *t = tmpbuf; + + t += sprintf(tmpbuf, "%d CAPIMSG", chanp->chan); + t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length); + t--; + *t= 0; + HiSax_putstatus(chanp->cs, "Ch", "%d CAPIMSG %s", chanp->chan, tmpbuf); +} + +void +lli_got_fac_req(struct Channel *chanp, capi_msg *cm) { + if ((cm->para[0] != 3) || (cm->para[1] != 0)) + return; + if (cm->para[2]<3) + return; + if (cm->para[4] != 0) + return; + switch(cm->para[3]) { + case 4: /* Suspend */ + if (cm->para[5]) { + strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1); + FsmEvent(&chanp->fi, EV_SUSPEND, cm); + } + break; + case 5: /* Resume */ + if (cm->para[5]) { + strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1); + if (chanp->fi.state == ST_NULL) { + FsmEvent(&chanp->fi, EV_RESUME, cm); + } else { + FsmDelTimer(&chanp->dial_timer, 72); + FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73); + } + } + break; + } +} + +void +lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg *cm) { + if ((cs->typ == ISDN_CTYPE_ELSA) || (cs->typ == ISDN_CTYPE_ELSA_PNP) || + (cs->typ == ISDN_CTYPE_ELSA_PCI)) { + if (cs->hw.elsa.MFlag) { + cs->cardmsg(cs, CARD_AUX_IND, cm->para); + } + } +} +#endif + int HiSax_command(isdn_ctrl * ic) { struct IsdnCardState *csta = hisax_findcard(ic->driver); struct Channel *chanp; - char tmp[128]; int i; - unsigned int num; + u_int num; + u_long adr; if (!csta) { printk(KERN_ERR @@ -1745,32 +1764,35 @@ ic->command, ic->driver); return -ENODEV; } + switch (ic->command) { case (ISDN_CMD_SETEAZ): chanp = csta->channel + ic->arg; - if (chanp->debug & 1) - link_debug(chanp, "SETEAZ", 1); break; + case (ISDN_CMD_SETL2): chanp = csta->channel + (ic->arg & 0xff); - if (chanp->debug & 1) { - sprintf(tmp, "SETL2 card %d %ld", csta->cardnr + 1, - ic->arg >> 8); - link_debug(chanp, tmp, 1); - } + if (chanp->debug & 1) + link_debug(chanp, 1, "SETL2 card %d %ld", + csta->cardnr + 1, ic->arg >> 8); chanp->l2_protocol = ic->arg >> 8; break; + case (ISDN_CMD_SETL3): + chanp = csta->channel + (ic->arg & 0xff); + if (chanp->debug & 1) + link_debug(chanp, 1, "SETL3 card %d %ld", + csta->cardnr + 1, ic->arg >> 8); + chanp->l3_protocol = ic->arg >> 8; + break; case (ISDN_CMD_DIAL): chanp = csta->channel + (ic->arg & 0xff); - if (chanp->debug & 1) { - sprintf(tmp, "DIAL %s -> %s (%d,%d)", + if (chanp->debug & 1) + link_debug(chanp, 1, "DIAL %s -> %s (%d,%d)", ic->parm.setup.eazmsn, ic->parm.setup.phone, - ic->parm.setup.si1, ic->parm.setup.si2); - link_debug(chanp, tmp, 1); - } - chanp->para.setup = ic->parm.setup; - if (!strcmp(chanp->para.setup.eazmsn, "0")) - chanp->para.setup.eazmsn[0] = '\0'; + ic->parm.setup.si1, ic->parm.setup.si2); + chanp->setup = ic->parm.setup; + if (!strcmp(chanp->setup.eazmsn, "0")) + chanp->setup.eazmsn[0] = '\0'; /* this solution is dirty and may be change, if * we make a callreference based callmanager */ if (chanp->fi.state == ST_NULL) { @@ -1783,57 +1805,56 @@ case (ISDN_CMD_ACCEPTB): chanp = csta->channel + ic->arg; if (chanp->debug & 1) - link_debug(chanp, "ACCEPTB", 1); + link_debug(chanp, 1, "ACCEPTB"); FsmEvent(&chanp->fi, EV_ACCEPTB, NULL); break; case (ISDN_CMD_ACCEPTD): chanp = csta->channel + ic->arg; if (chanp->debug & 1) - link_debug(chanp, "ACCEPTD", 1); + link_debug(chanp, 1, "ACCEPTD"); FsmEvent(&chanp->fi, EV_ACCEPTD, NULL); break; case (ISDN_CMD_HANGUP): chanp = csta->channel + ic->arg; if (chanp->debug & 1) - link_debug(chanp, "HANGUP", 1); + link_debug(chanp, 1, "HANGUP"); FsmEvent(&chanp->fi, EV_HANGUP, NULL); break; - case (ISDN_CMD_SUSPEND): +#if 0 + case (CAPI_PUT_MESSAGE): chanp = csta->channel + ic->arg; - if (chanp->debug & 1) { - sprintf(tmp, "SUSPEND %s", ic->parm.num); - link_debug(chanp, tmp, 1); - } - FsmEvent(&chanp->fi, EV_SUSPEND, ic); - break; - case (ISDN_CMD_RESUME): - chanp = csta->channel + ic->arg; - if (chanp->debug & 1) { - sprintf(tmp, "RESUME %s", ic->parm.num); - link_debug(chanp, tmp, 1); + if (chanp->debug & 1) + capi_debug(chanp, &ic->parm.cmsg); + if (ic->parm.cmsg.Length < 8) + break; + switch(ic->parm.cmsg.Command) { + case CAPI_FACILITY: + if (ic->parm.cmsg.Subcommand == CAPI_REQ) + lli_got_fac_req(chanp, &ic->parm.cmsg); + break; + case CAPI_MANUFACTURER: + if (ic->parm.cmsg.Subcommand == CAPI_REQ) + lli_got_manufacturer(chanp, csta, &ic->parm.cmsg); + break; + default: + break; } - FsmEvent(&chanp->fi, EV_RESUME, ic); break; +#endif case (ISDN_CMD_LOCK): HiSax_mod_inc_use_count(); #ifdef MODULE - if (csta->channel[0].debug & 1024) { - jiftime(tmp, jiffies); - i = strlen(tmp); - sprintf(tmp + i, " LOCK modcnt %lx\n", MOD_USE_COUNT); - HiSax_putstatus(csta, tmp); - } + if (csta->channel[0].debug & 0x400) + HiSax_putstatus(csta, " LOCK ", "modcnt %lx", + MOD_USE_COUNT); #endif /* MODULE */ break; case (ISDN_CMD_UNLOCK): HiSax_mod_dec_use_count(); #ifdef MODULE - if (csta->channel[0].debug & 1024) { - jiftime(tmp, jiffies); - i = strlen(tmp); - sprintf(tmp + i, " UNLOCK modcnt %lx\n", MOD_USE_COUNT); - HiSax_putstatus(csta, tmp); - } + if (csta->channel[0].debug & 0x400) + HiSax_putstatus(csta, " UNLOCK ", "modcnt %lx", + MOD_USE_COUNT); #endif /* MODULE */ break; case (ISDN_CMD_IOCTL): @@ -1846,22 +1867,19 @@ case (1): num = *(unsigned int *) ic->parm.num; distr_debug(csta, num); - sprintf(tmp, "debugging flags card %d set to %x\n", + printk(KERN_DEBUG "HiSax: debugging flags card %d set to %x\n", csta->cardnr + 1, num); - HiSax_putstatus(csta, tmp); - printk(KERN_DEBUG "HiSax: %s", tmp); + HiSax_putstatus(csta, "debugging flags ", + "card %d set to %x", csta->cardnr + 1, num); break; case (2): num = *(unsigned int *) ic->parm.num; - i = num >> 8; - if (i >= 2) - break; - chanp = csta->channel + i; - chanp->impair = num & 0xff; - if (chanp->debug & 1) { - sprintf(tmp, "IMPAIR %x", chanp->impair); - link_debug(chanp, tmp, 1); - } + csta->channel[0].b_st->l1.delay = num; + csta->channel[1].b_st->l1.delay = num; + HiSax_putstatus(csta, "delay ", "card %d set to %d ms", + csta->cardnr + 1, num); + printk(KERN_DEBUG "HiSax: delay card %d set to %d ms\n", + csta->cardnr + 1, num); break; case (3): for (i = 0; i < *(unsigned int *) ic->parm.num; i++) @@ -1872,36 +1890,92 @@ HiSax_mod_inc_use_count(); break; case (5): /* set card in leased mode */ - csta->channel[0].leased = 1; - csta->channel[1].leased = 1; - sprintf(tmp, "card %d set into leased mode\n", - csta->cardnr + 1); - HiSax_putstatus(csta, tmp); + num = *(unsigned int *) ic->parm.num; + if ((num <1) || (num > 2)) { + HiSax_putstatus(csta, "Set LEASED ", + "wrong channel %d", num); + printk(KERN_WARNING "HiSax: Set LEASED wrong channel %d\n", + num); + } else { + num--; + chanp = csta->channel +num; + chanp->leased = 1; + HiSax_putstatus(csta, "Card", + "%d channel %d set leased mode\n", + csta->cardnr + 1, num + 1); + chanp->d_st->l1.l1l2 = leased_l1l2; + chanp->d_st->lli.l4l3 = leased_l4l3; + chanp->d_st->lli.l4l3(chanp->d_st, + DL_ESTABLISH | REQUEST, NULL); + } + break; + case (6): /* set B-channel test loop */ + num = *(unsigned int *) ic->parm.num; + if (csta->stlist) + csta->stlist->l2.l2l1(csta->stlist, + PH_TESTLOOP | REQUEST, (void *) (long)num); + break; + case (7): /* set card in PTP mode */ + num = *(unsigned int *) ic->parm.num; + if (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) { + printk(KERN_ERR "HiSax PTP mode only with one TEI possible\n"); + } else if (num) { + test_and_set_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag); + test_and_set_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag); + csta->channel[0].d_st->l2.tei = 0; + HiSax_putstatus(csta, "set card ", "in PTP mode"); + printk(KERN_DEBUG "HiSax: set card in PTP mode\n"); + printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n"); + test_and_set_bit(FLG_START_D, &csta->channel[0].Flags); + test_and_set_bit(FLG_START_D, &csta->channel[1].Flags); + csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st, + DL_ESTABLISH | REQUEST, NULL); + } else { + test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag); + test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag); + HiSax_putstatus(csta, "set card ", "in PTMP mode"); + printk(KERN_DEBUG "HiSax: set card in PTMP mode\n"); + } + break; + case (8): /* set card in FIXED TEI mode */ + num = *(unsigned int *) ic->parm.num; + chanp = csta->channel + (num & 1); + num = num >>1; + test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag); + chanp->d_st->l2.tei = num; + HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num); + printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n", + num); + break; + case (9): /* load firmware */ + memcpy(&adr, ic->parm.num, sizeof(ulong)); + csta->cardmsg(csta, CARD_LOAD_FIRM, + (void *) adr); break; #ifdef MODULE case (55): -#if (LINUX_VERSION_CODE < 0x020111) - MOD_USE_COUNT = MOD_VISITED; -#else MOD_USE_COUNT = 0; -#endif HiSax_mod_inc_use_count(); break; #endif /* MODULE */ case (11): + num = csta->debug & DEB_DLOG_HEX; csta->debug = *(unsigned int *) ic->parm.num; - sprintf(tmp, "l1 debugging flags card %d set to %x\n", - csta->cardnr + 1, csta->debug); - HiSax_putstatus(cards[0].sp, tmp); - printk(KERN_DEBUG "HiSax: %s", tmp); + csta->debug |= num; + HiSax_putstatus(cards[0].cs, "l1 debugging ", + "flags card %d set to %x", + csta->cardnr + 1, csta->debug); + printk(KERN_DEBUG "HiSax: l1 debugging flags card %d set to %x\n", + csta->cardnr + 1, csta->debug); break; case (13): - csta->channel[0].is.l3.debug = *(unsigned int *) ic->parm.num; - csta->channel[1].is.l3.debug = *(unsigned int *) ic->parm.num; - sprintf(tmp, "l3 debugging flags card %d set to %x\n", + csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num; + csta->channel[1].d_st->l3.debug = *(unsigned int *) ic->parm.num; + HiSax_putstatus(cards[0].cs, "l3 debugging ", + "flags card %d set to %x\n", csta->cardnr + 1, + *(unsigned int *) ic->parm.num); + printk(KERN_DEBUG "HiSax: l3 debugging flags card %d set to %x\n", csta->cardnr + 1, *(unsigned int *) ic->parm.num); - HiSax_putstatus(cards[0].sp, tmp); - printk(KERN_DEBUG "HiSax: %s", tmp); break; default: printk(KERN_DEBUG "HiSax: invalid ioclt %d\n", @@ -1925,7 +1999,6 @@ int len = skb->len; unsigned long flags; struct sk_buff *nskb; - char tmp[64]; if (!csta) { printk(KERN_ERR @@ -1933,38 +2006,37 @@ return -ENODEV; } chanp = csta->channel + chan; - st = &chanp->ds; + st = chanp->b_st; if (!chanp->data_open) { - link_debug(chanp, "writebuf: channel not open", 1); + link_debug(chanp, 1, "writebuf: channel not open"); return -EIO; } if (len > MAX_DATA_SIZE) { - sprintf(tmp, "writebuf: packet too large (%d bytes)", len); - printk(KERN_WARNING "HiSax_%s !\n", tmp); - link_debug(chanp, tmp, 1); + link_debug(chanp, 1, "writebuf: packet too large (%d bytes)", len); + printk(KERN_WARNING "HiSax_writebuf: packet too large (%d bytes) !\n", + len); return -EINVAL; } if (len) { - if ((len + csta->hs[chanp->hscx].tx_cnt) > MAX_DATA_MEM) { + if ((len + chanp->bcs->tx_cnt) > MAX_DATA_MEM) { /* Must return 0 here, since this is not an error * but a temporary lack of resources. */ - if (chanp->debug & 2048) { - sprintf(tmp, "writebuf: no buffers for %d bytes", len); - link_debug(chanp, tmp, 1); - } + if (chanp->debug & 0x800) + link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len); return 0; } save_flags(flags); cli(); nskb = skb_clone(skb, GFP_ATOMIC); if (nskb) { - if (chanp->lc_b.l2_establish) { - csta->hs[chanp->hscx].tx_cnt += len + st->l2.ihsize; - chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, nskb); - } else { - csta->hs[chanp->hscx].tx_cnt += len; - chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, nskb); +// if (!ack) +// nskb->pkt_type = PACKET_NOACK; + if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I) + st->l3.l3l2(st, DL_DATA | REQUEST, nskb); + else { + chanp->bcs->tx_cnt += len; + st->l2.l2l1(st, PH_DATA | REQUEST, nskb); } dev_kfree_skb(skb, FREE_WRITE); } else diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/cert.c linux/drivers/isdn/hisax/cert.c --- v2.0.35/linux/drivers/isdn/hisax/cert.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/cert.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,52 @@ +/* $Id: cert.c,v 1.2.2.1 1998/11/03 21:46:37 keil Exp $ + + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * + * $Log: cert.c,v $ + * Revision 1.2.2.1 1998/11/03 21:46:37 keil + * first version + * + * + */ + +#include + +int +certification_check(int output) { + +#ifdef CERTIFICATION +#if CERTIFICATION == 0 + if (output) { + printk(KERN_INFO "HiSax: Approval certification valid\n"); + printk(KERN_INFO "HiSax: Approved with ELSA Quickstep series cards\n"); + 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"); + } + return(0); +#endif +#if CERTIFICATION == 1 + if (output) { + printk(KERN_INFO "HiSax: Approval certification failed because of\n"); + printk(KERN_INFO "HiSax: unauthorized source code changes\n"); + } + return(1); +#endif +#if CERTIFICATION == 127 + if (output) { + printk(KERN_INFO "HiSax: Approval certification not possible\n"); + printk(KERN_INFO "HiSax: because \"md5sum\" is not available\n"); + } + return(2); +#endif +#else + if (output) { + printk(KERN_INFO "HiSax: Certification not verified\n"); + } + return(3); +#endif +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c --- v2.0.35/linux/drivers/isdn/hisax/config.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/config.c Sun Nov 15 10:32:58 1998 @@ -1,58 +1,97 @@ -/* $Id: config.c,v 1.15 1997/04/06 22:57:24 keil Exp $ +/* $Id: config.c,v 1.15.2.24 1998/11/11 13:37:53 keil Exp $ - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * * * $Log: config.c,v $ - * Revision 1.15 1997/04/06 22:57:24 keil - * Hisax version 2.1 + * Revision 1.15.2.24 1998/11/11 13:37:53 keil + * fix typo * - * Revision 1.14 1997/03/25 23:11:22 keil - * US NI-1 protocol + * Revision 1.15.2.23 1998/11/11 11:04:36 keil + * update version * - * Revision 1.13 1997/03/23 21:45:49 keil - * Add support for ELSA PCMCIA + * Revision 1.15.2.22 1998/11/08 13:00:56 niemann + * Added doc for Sedlbauer ISDN cards, + * added info for downloading firmware (Sedlbauer speed fax+) * - * Revision 1.12 1997/03/11 21:01:43 keil - * nzproto is only used with modules + * Revision 1.15.2.21 1998/11/05 21:13:46 keil + * minor fixes * - * Revision 1.11 1997/02/14 12:23:12 fritz - * Added support for new insmod parameter handling. + * Revision 1.15.2.20 1998/11/04 11:50:32 fritz + * Make compile with modversions enabled. * - * Revision 1.10 1997/02/14 09:22:09 keil - * Final 2.0 version + * Revision 1.15.2.19 1998/11/03 00:06:05 keil + * certification related changes + * fixed logging for smaller stack use * - * Revision 1.9 1997/02/10 11:45:09 fritz - * More changes for Kernel 2.1.X compatibility. + * Revision 1.15.2.18 1998/10/13 10:27:26 keil + * New cards, minor fixes * - * Revision 1.8 1997/02/09 00:28:05 keil - * new interface handling, one interface per card - * default protocol now works again + * Revision 1.15.2.17 1998/10/11 19:31:31 niemann + * Fixed problems with CONFIG_MODVERSIONS for sedlbauer cards * - * Revision 1.7 1997/01/27 15:56:57 keil - * Teles PCMCIA ITK ix1 micro added + * Revision 1.15.2.16 1998/09/27 13:05:48 keil + * Apply most changes from 2.1.X (HiSax 3.1) * - * Revision 1.6 1997/01/21 22:17:56 keil - * new module load syntax + * Revision 1.15.2.15 1998/09/12 18:43:56 niemann + * Added new card: Sedlbauer ISDN-Controller PC/104 * - * Revision 1.5 1997/01/09 18:28:20 keil - * cosmetic cleanups + * Revision 1.15.2.14 1998/08/25 14:01:27 calle + * Ported driver for AVM Fritz!Card PCI from the 2.1 tree. + * I could not test it. * - * Revision 1.4 1996/11/05 19:35:17 keil - * using config.h; some spelling fixes + * Revision 1.15.2.13 1998/07/30 20:51:24 niemann + * Fixed Sedlbauer Speed Card PCMCIA missing isdnl3new * - * Revision 1.3 1996/10/23 17:23:28 keil - * default config changes + * Revision 1.15.2.12 1998/07/15 14:43:29 calle + * Support for AVM passive PCMCIA cards: + * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 * - * Revision 1.2 1996/10/23 11:58:48 fritz - * Changed default setup to reflect user's selection of supported - * cards/protocols. + * Revision 1.15.2.11 1998/05/27 18:05:07 keil + * HiSax 3.0 * - * Revision 1.1 1996/10/13 20:04:51 keil - * Initial revision + * Revision 1.15.2.10 1998/04/11 18:43:13 keil + * New cards * + * Revision 1.15.2.9 1998/03/07 23:15:12 tsbogend + * made HiSax working on Linux/Alpha * + * Revision 1.15.2.8 1998/02/11 19:21:37 keil + * fix typo + * + * Revision 1.15.2.7 1998/02/11 14:23:08 keil + * support for Dr Neuhaus Niccy PnP and PCI + * + * Revision 1.15.2.6 1998/02/09 11:21:19 keil + * Sedlbauer PCMCIA support from Marcus Niemann + * + * Revision 1.15.2.5 1998/01/27 23:28:48 keil + * v2.8 + * + * Revision 1.15.2.4 1998/01/27 22:33:53 keil + * dynalink ----> asuscom + * + * Revision 1.15.2.3 1998/01/11 22:55:15 keil + * 16.3c support + * + * Revision 1.15.2.2 1997/11/15 18:55:46 keil + * New init, new cards + * + * Revision 1.15.2.1 1997/10/17 22:13:40 keil + * update to last hisax version + * + * Revision 2.2 1997/09/11 17:24:46 keil + * Add new cards + * + * Revision 2.1 1997/07/27 21:41:35 keil + * version change + * + * Revision 2.0 1997/06/26 11:06:28 keil + * New card and L1 interface. + * Eicon.Diehl Diva and Dynalink IS64PH support + * + * old changes removed /KKe * */ #include @@ -60,6 +99,13 @@ #include #include #include "hisax.h" +#include +#include +#define kstat_irqs( PAR ) kstat.interrupts[PAR] +#include +#include +#define HISAX_STATUS_BUFSIZE 4096 +#define INCLUDE_INLINE_FUNCS /* * This structure array contains one entry per card. An entry looks @@ -68,55 +114,202 @@ * { type, protocol, p0, p1, p2, NULL } * * type - * 1 Teles 16.0 p0=irq p1=membase p2=iobase - * 2 Teles 8.0 p0=irq p1=membase - * 3 Teles 16.3 p0=irq p1=iobase - * 4 Creatix PNP p0=irq p1=IO0 (ISAC) p2=IO1 (HSCX) - * 5 AVM A1 (Fritz) p0=irq p1=iobase - * 6 ELSA PC [p0=iobase] or nothing (autodetect) - * 7 ELSA Quickstep p0=irq p1=iobase - * ELSA PCMCIA p0=irq p1=iobase - * 8 Teles PCMCIA p0=irq p1=iobase - * 9 ITK ix1-micro p0=irq p1=iobase - * + * 1 Teles 16.0 p0=irq p1=membase p2=iobase + * 2 Teles 8.0 p0=irq p1=membase + * 3 Teles 16.3 p0=irq p1=iobase + * 4 Creatix PNP p0=irq p1=IO0 (ISAC) p2=IO1 (HSCX) + * 5 AVM A1 (Fritz) p0=irq p1=iobase + * 6 ELSA PC [p0=iobase] or nothing (autodetect) + * 7 ELSA Quickstep p0=irq p1=iobase + * 8 Teles PCMCIA p0=irq p1=iobase + * 9 ITK ix1-micro p0=irq p1=iobase + * 10 ELSA PCMCIA p0=irq p1=iobase + * 11 Eicon.Diehl Diva p0=irq p1=iobase + * 12 Asuscom ISDNLink p0=irq p1=iobase + * 13 Teleint p0=irq p1=iobase + * 14 Teles 16.3c p0=irq p1=iobase + * 15 Sedlbauer speed p0=irq p1=iobase + * 15 Sedlbauer PC/104 p0=irq p1=iobase + * 15 Sedlbauer speed pci no parameter + * 16 USR Sportster internal p0=irq p1=iobase + * 17 MIC card p0=irq p1=iobase + * 18 ELSA Quickstep 1000PCI no parameter + * 19 Compaq ISDN S0 ISA card p0=irq p1=IO0 (HSCX) p2=IO1 (ISAC) p3=IO2 + * 20 Travers Technologies NETjet PCI card + * 21 TELES PCI no parameter + * 22 Sedlbauer Speed Star p0=irq p1=iobase + * 23 reserved + * 24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only) + * 25 Teles S0Box p0=irq p1=iobase (from isapnp setup) + * 26 AVM A1 PCMCIA (Fritz) p0=irq p1=iobase + * 27 AVM PnP/PCI p0=irq p1=iobase (PCI no parameter) + * 28 Sedlbauer Speed Fax+ p0=irq p1=iobase (from isapnp setup) * * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1 * * */ -#ifdef CONFIG_HISAX_ELSA_PCC +const char *CardType[] = +{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", "Creatix/Teles PnP", + "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2", + "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c", + "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI", + "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)", + "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI", + "Sedlbauer Speed Fax +" +}; + +#ifdef CONFIG_HISAX_ELSA #define DEFAULT_CARD ISDN_CTYPE_ELSA -#define DEFAULT_CFG {0,0,0} -#endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA -#define DEFAULT_CARD ISDN_CTYPE_ELSA_QS1000 -#define DEFAULT_CFG {3,0x2f8,0} +#define DEFAULT_CFG {0,0,0,0} +int elsa_init_pcmcia(void*, int, int*, int); +static struct symbol_table hisax_syms_elsa = { +#include + X(elsa_init_pcmcia), +#include +}; +void register_elsa_symbols(void) { + register_symtab(&hisax_syms_elsa); +} #endif #ifdef CONFIG_HISAX_AVM_A1 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_A1 -#define DEFAULT_CFG {10,0x340,0} +#define DEFAULT_CFG {10,0x340,0,0} +#endif + +#ifdef CONFIG_HISAX_AVM_A1_PCMCIA +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA +#define DEFAULT_CFG {11,0x170,0,0} +int avm_a1_init_pcmcia(void*, int, int*, int); +void HiSax_closecard(int cardnr); +static struct symbol_table hisax_syms_avm_a1= { +#include + X(avm_a1_init_pcmcia), + X(HiSax_closecard), +#include +}; +void register_avm_a1_symbols(void) { + register_symtab(&hisax_syms_avm_a1); +} +#endif +#ifdef CONFIG_HISAX_FRITZPCI +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_FRITZPCI +#define DEFAULT_CFG {0,0,0,0} #endif #ifdef CONFIG_HISAX_16_3 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_16_3 -#define DEFAULT_CFG {15,0x180,0} +#define DEFAULT_CFG {15,0x180,0,0} +#endif +#ifdef CONFIG_HISAX_S0BOX +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_S0BOX +#define DEFAULT_CFG {7,0x378,0,0} #endif #ifdef CONFIG_HISAX_16_0 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_16_0 -#define DEFAULT_CFG {15,0xd0000,0xd80} +#define DEFAULT_CFG {15,0xd0000,0xd80,0} +#endif + +#ifdef CONFIG_HISAX_TELESPCI +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELESPCI +#define DEFAULT_CFG {0,0,0,0} #endif #ifdef CONFIG_HISAX_IX1MICROR2 #undef DEFAULT_CARD #undef DEFAULT_CFG #define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2 -#define DEFAULT_CFG {5,0x390,0} +#define DEFAULT_CFG {5,0x390,0,0} +#endif + +#ifdef CONFIG_HISAX_DIEHLDIVA +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA +#define DEFAULT_CFG {0,0x0,0,0} +#endif + +#ifdef CONFIG_HISAX_ASUSCOM +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM +#define DEFAULT_CFG {5,0x200,0,0} +#endif + +#ifdef CONFIG_HISAX_TELEINT +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELEINT +#define DEFAULT_CFG {5,0x300,0,0} +#endif + +#ifdef CONFIG_HISAX_SEDLBAUER +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER +#define DEFAULT_CFG {11,0x270,0,0} +int sedl_init_pcmcia(void*, int, int*, int); +static struct symbol_table hisax_syms_sedl= { +#include + X(sedl_init_pcmcia), +#include +}; +#endif + +#ifdef CONFIG_HISAX_SPORTSTER +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_SPORTSTER +#define DEFAULT_CFG {7,0x268,0,0} +#endif + +#ifdef CONFIG_HISAX_MIC +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_MIC +#define DEFAULT_CFG {12,0x3e0,0,0} +#endif + +#ifdef CONFIG_HISAX_NETJET +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_NETJET +#define DEFAULT_CFG {0,0,0,0} +#endif + +#ifdef CONFIG_HISAX_TELES3C +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_TELES3C +#define DEFAULT_CFG {5,0x500,0,0} +#endif + +#ifdef CONFIG_HISAX_AMD7930 +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_AMD7930 +#define DEFAULT_CFG {12,0x3e0,0,0} +#endif + +#ifdef CONFIG_HISAX_NICCY +#undef DEFAULT_CARD +#undef DEFAULT_CFG +#define DEFAULT_CARD ISDN_CTYPE_NICCY +#define DEFAULT_CFG {0,0x0,0,0} #endif #ifdef CONFIG_HISAX_1TR6 @@ -144,13 +337,13 @@ #endif #define FIRST_CARD { \ - DEFAULT_CARD, \ - DEFAULT_PROTO, \ - DEFAULT_CFG, \ - NULL, \ + DEFAULT_CARD, \ + DEFAULT_PROTO, \ + DEFAULT_CFG, \ + NULL, \ } -#define EMPTY_CARD {0, DEFAULT_PROTO, {0, 0, 0}, NULL} +#define EMPTY_CARD {0, DEFAULT_PROTO, {0, 0, 0, 0}, NULL} struct IsdnCard cards[] = { @@ -162,65 +355,65 @@ EMPTY_CARD, EMPTY_CARD, EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, - EMPTY_CARD, }; -static char HiSaxID[96] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ -"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ +static char HiSaxID[64] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \ -"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; -char *HiSax_id = HiSaxID; +"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +char *HiSax_id HISAX_INITDATA = HiSaxID; #ifdef MODULE /* Variables for insmod */ -int type[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -int protocol[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -int io[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ -int io0[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -int io1[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -#endif -int irq[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -int mem[] = -{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -char *id = HiSaxID; +static int type[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static int protocol[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static int io[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +#undef IO0_IO1 +#ifdef CONFIG_HISAX_16_3 +#define IO0_IO1 +#endif +#ifdef CONFIG_HISAX_NICCY +#undef IO0_IO1 +#define IO0_IO1 +#endif +#ifdef IO0_IO1 +static int io0[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static int io1[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +#endif +static int irq[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static int mem[] HISAX_INITDATA = +{0, 0, 0, 0, 0, 0, 0, 0}; +static char *id HISAX_INITDATA = HiSaxID; #if (LINUX_VERSION_CODE > 0x020111) MODULE_AUTHOR("Karsten Keil"); -MODULE_PARM(type, "1-16i"); -MODULE_PARM(protocol, "1-16i"); -MODULE_PARM(io, "1-16i"); -MODULE_PARM(irq, "1-16i"); -MODULE_PARM(mem, "1-16i"); +MODULE_PARM(type, "1-8i"); +MODULE_PARM(protocol, "1-8i"); +MODULE_PARM(io, "1-8i"); +MODULE_PARM(irq, "1-8i"); +MODULE_PARM(mem, "1-8i"); MODULE_PARM(id, "s"); #ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ -MODULE_PARM(io0, "1-16i"); -MODULE_PARM(io1, "1-16i"); -#endif -#endif +MODULE_PARM(io0, "1-8i"); +MODULE_PARM(io1, "1-8i"); +#endif /* CONFIG_HISAX_16_3 */ +#endif /* (LINUX_VERSION_CODE > 0x020111) */ +#endif /* MODULE */ -#endif +int nrcards; extern char *l1_revision; extern char *l2_revision; extern char *l3_revision; -extern char *l4_revision; +extern char *lli_revision; extern char *tei_revision; -char * -HiSax_getrev(const char *revision) +HISAX_INITFUNC(char * +HiSax_getrev(const char *revision)) { char *rev; char *p; @@ -234,7 +427,29 @@ return rev; } -int nrcards; +HISAX_INITFUNC(void +HiSaxVersion(void)) +{ + char tmp[64]; + + printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n"); +#ifdef MODULE + printk(KERN_INFO "HiSax: Version 3.1 (module)\n"); +#else + printk(KERN_INFO "HiSax: Version 3.1 (kernel)\n"); +#endif + strcpy(tmp, l1_revision); + printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp)); + strcpy(tmp, l2_revision); + printk(KERN_INFO "HiSax: Layer2 Revision %s\n", HiSax_getrev(tmp)); + strcpy(tmp, tei_revision); + printk(KERN_INFO "HiSax: TeiMgr Revision %s\n", HiSax_getrev(tmp)); + strcpy(tmp, l3_revision); + printk(KERN_INFO "HiSax: Layer3 Revision %s\n", HiSax_getrev(tmp)); + strcpy(tmp, lli_revision); + printk(KERN_INFO "HiSax: LinkLayer Revision %s\n", HiSax_getrev(tmp)); + certification_check(1); +} void HiSax_mod_dec_use_count(void) @@ -251,15 +466,15 @@ #ifdef MODULE #define HiSax_init init_module #else -void -HiSax_setup(char *str, int *ints) +__initfunc(void +HiSax_setup(char *str, int *ints)) { int i, j, argc; argc = ints[0]; i = 0; j = 1; - while (argc && (i < 16)) { + while (argc && (i < HISAX_MAX_CARDS)) { if (argc) { cards[i].typ = ints[j]; j++; @@ -297,35 +512,783 @@ } #endif +#if CARD_TELES0 +extern int setup_teles0(struct IsdnCard *card); +#endif + +#if CARD_TELES3 +extern int setup_teles3(struct IsdnCard *card); +#endif + +#if CARD_S0BOX +extern int setup_s0box(struct IsdnCard *card); +#endif + +#if CARD_TELESPCI +extern int setup_telespci(struct IsdnCard *card); +#endif + +#if CARD_AVM_A1 +extern int setup_avm_a1(struct IsdnCard *card); +#endif + +#if CARD_AVM_A1_PCMCIA +extern int setup_avm_a1_pcmcia(struct IsdnCard *card); +#endif + +#if CARD_FRITZPCI +extern int setup_avm_pcipnp(struct IsdnCard *card); +#endif + +#if CARD_ELSA +extern int setup_elsa(struct IsdnCard *card); +#endif + +#if CARD_IX1MICROR2 +extern int setup_ix1micro(struct IsdnCard *card); +#endif + +#if CARD_DIEHLDIVA +extern int setup_diva(struct IsdnCard *card); +#endif + +#if CARD_ASUSCOM +extern int setup_asuscom(struct IsdnCard *card); +#endif + +#if CARD_TELEINT +extern int setup_TeleInt(struct IsdnCard *card); +#endif + +#if CARD_SEDLBAUER +extern int setup_sedlbauer(struct IsdnCard *card); +#endif + +#if CARD_SPORTSTER +extern int setup_sportster(struct IsdnCard *card); +#endif + +#if CARD_MIC +extern int setup_mic(struct IsdnCard *card); +#endif + +#if CARD_NETJET +extern int setup_netjet(struct IsdnCard *card); +#endif + +#if CARD_TELES3C +extern int setup_t163c(struct IsdnCard *card); +#endif + +#if CARD_AMD7930 +extern int setup_amd7930(struct IsdnCard *card); +#endif + +#if CARD_NICCY +extern int setup_niccy(struct IsdnCard *card); +#endif + +/* + * Find card with given driverId + */ +static inline struct IsdnCardState +*hisax_findcard(int driverid) +{ + int i; + + for (i = 0; i < nrcards; i++) + if (cards[i].cs) + if (cards[i].cs->myid == driverid) + return (cards[i].cs); + return (NULL); +} + int -HiSax_init(void) +HiSax_readstatus(u_char * buf, int len, int user, int id, int channel) +{ + int count; + u_char *p = buf; + struct IsdnCardState *cs = hisax_findcard(id); + + if (cs) { + if (len > HISAX_STATUS_BUFSIZE) { + printk(KERN_WARNING "HiSax: status overflow readstat %d/%d", + len, HISAX_STATUS_BUFSIZE); + return -ENODEV; + } + count = cs->status_end - cs->status_read +1; + if (count >= len) + count = len; + if (user) + copy_to_user(p, cs->status_read, count); + else + memcpy(p, cs->status_read, count); + cs->status_read += count; + if (cs->status_read > cs->status_end) + cs->status_read = cs->status_buf; + p += count; + count = len - count; + if (count) { + if (user) + copy_to_user(p, cs->status_read, count); + else + memcpy(p, cs->status_read, count); + cs->status_read += count; + } + return len; + } else { + printk(KERN_ERR + "HiSax: if_readstatus called with invalid driverId!\n"); + return -ENODEV; + } +} + +inline int +jiftime(char *s, long mark) +{ + s += 8; + + *s-- = '\0'; + *s-- = mark % 10 + '0'; + mark /= 10; + *s-- = mark % 10 + '0'; + mark /= 10; + *s-- = '.'; + *s-- = mark % 10 + '0'; + mark /= 10; + *s-- = mark % 6 + '0'; + mark /= 6; + *s-- = ':'; + *s-- = mark % 10 + '0'; + mark /= 10; + *s-- = mark % 10 + '0'; + return(8); +} + +static u_char tmpbuf[HISAX_STATUS_BUFSIZE]; + +void +VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args) +{ +/* if head == NULL the fmt contains the full info */ + + long flags; + int count, i; + u_char *p; + isdn_ctrl ic; + int len; + + save_flags(flags); + cli(); + p = tmpbuf; + if (head) { + p += jiftime(p, jiffies); + p += sprintf(p, " %s", head); + p += vsprintf(p, fmt, args); + *p++ = '\n'; + *p = 0; + len = p - tmpbuf; + p = tmpbuf; + } else { + p = fmt; + len = strlen(fmt); + } + if (!cs) { + printk(KERN_WARNING "HiSax: No CardStatus for message %s", p); + restore_flags(flags); + return; + } + if (len > HISAX_STATUS_BUFSIZE) { + printk(KERN_WARNING "HiSax: status overflow %d/%d", + len, HISAX_STATUS_BUFSIZE); + restore_flags(flags); + return; + } + count = len; + i = cs->status_end - cs->status_write +1; + if (i >= len) + i = len; + len -= i; + memcpy(cs->status_write, p, i); + cs->status_write += i; + if (cs->status_write > cs->status_end) + cs->status_write = cs->status_buf; + p += i; + if (len) { + memcpy(cs->status_write, p, len); + cs->status_write += len; + } +#ifdef KERNELSTACK_DEBUG + i = (ulong)&len - current->kernel_stack_page; + sprintf(tmpbuf, "kstack %s %lx use %ld\n", current->comm, + current->kernel_stack_page, i); + len = strlen(tmpbuf); + for (p = tmpbuf, i = len; i > 0; i--, p++) { + *cs->status_write++ = *p; + if (cs->status_write > cs->status_end) + cs->status_write = cs->status_buf; + count++; + } +#endif + restore_flags(flags); + if (count) { + ic.command = ISDN_STAT_STAVAIL; + ic.driver = cs->myid; + ic.arg = count; + cs->iif.statcallb(&ic); + } +} + +void +HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + VHiSax_putstatus(cs, head, fmt, args); + va_end(args); +} + +int +ll_run(struct IsdnCardState *cs) +{ + long flags; + isdn_ctrl ic; + + save_flags(flags); + cli(); + ic.driver = cs->myid; + ic.command = ISDN_STAT_RUN; + cs->iif.statcallb(&ic); + restore_flags(flags); + return 0; +} + +void +ll_stop(struct IsdnCardState *cs) +{ + isdn_ctrl ic; + + ic.command = ISDN_STAT_STOP; + ic.driver = cs->myid; + cs->iif.statcallb(&ic); + CallcFreeChan(cs); +} + +static void +ll_unload(struct IsdnCardState *cs) +{ + isdn_ctrl ic; + + ic.command = ISDN_STAT_UNLOAD; + ic.driver = cs->myid; + cs->iif.statcallb(&ic); + if (cs->status_buf) + kfree(cs->status_buf); + cs->status_read = NULL; + cs->status_write = NULL; + cs->status_end = NULL; + kfree(cs->dlog); +} + +static void +closecard(int cardnr) +{ + struct IsdnCardState *csta = cards[cardnr].cs; + + if (csta->bcs->BC_Close != NULL) { + csta->bcs->BC_Close(csta->bcs + 1); + csta->bcs->BC_Close(csta->bcs); + } + + if (csta->rcvbuf) { + kfree(csta->rcvbuf); + csta->rcvbuf = NULL; + } + discard_queue(&csta->rq); + discard_queue(&csta->sq); + if (csta->tx_skb) { + dev_kfree_skb(csta->tx_skb, FREE_WRITE); + csta->tx_skb = NULL; + } + if (csta->mon_rx) { + kfree(csta->mon_rx); + csta->mon_rx = NULL; + } + if (csta->mon_tx) { + kfree(csta->mon_tx); + csta->mon_tx = NULL; + } + csta->cardmsg(csta, CARD_RELEASE, NULL); + if (csta->dbusytimer.function != NULL) + del_timer(&csta->dbusytimer); + ll_unload(csta); +} + +HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs)) +{ + int irq_cnt, cnt = 3; + long flags; + + save_flags(flags); + cli(); + irq_cnt = kstat_irqs(cs->irq); + printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq, + irq_cnt); + if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) { + printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n", + cs->irq); + restore_flags(flags); + return(1); + } + while (cnt) { + cs->cardmsg(cs, CARD_INIT, NULL); + sti(); + current->state = TASK_INTERRUPTIBLE; + /* Timeout 10ms */ + current->timeout = jiffies + (10 * HZ) / 1000; + schedule(); + restore_flags(flags); + printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], + cs->irq, kstat_irqs(cs->irq)); + if (kstat_irqs(cs->irq) == irq_cnt) { + printk(KERN_WARNING + "%s: IRQ(%d) getting no interrupts during init %d\n", + CardType[cs->typ], cs->irq, 4 - cnt); + if (cnt == 1) { + free_irq(cs->irq, cs); + return (2); + } else { + cs->cardmsg(cs, CARD_RESET, NULL); + cnt--; + } + } else { + cs->cardmsg(cs, CARD_TEST, NULL); + return(0); + } + } + restore_flags(flags); + return(3); +} + +HISAX_INITFUNC(static int +checkcard(int cardnr, char *id, int *busy_flag)) +{ + long flags; + int ret = 0; + struct IsdnCard *card = cards + cardnr; + struct IsdnCardState *cs; + + save_flags(flags); + cli(); + if (!(cs = (struct IsdnCardState *) + kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for IsdnCardState(card %d)\n", + cardnr + 1); + restore_flags(flags); + return (0); + } + memset(cs, 0, sizeof(struct IsdnCardState)); + card->cs = cs; + cs->cardnr = cardnr; + cs->debug = L1_DEB_WARN; + cs->HW_Flags = 0; + cs->busy_flag = busy_flag; +#if TEI_PER_CARD +#else + test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags); +#endif + cs->protocol = card->protocol; + + if ((card->typ > 0) && (card->typ < 31)) { + if (!((1 << card->typ) & SUPORTED_CARDS)) { + printk(KERN_WARNING + "HiSax: Support for %s Card not selected\n", + CardType[card->typ]); + restore_flags(flags); + return (0); + } + } else { + printk(KERN_WARNING + "HiSax: Card Type %d out of range\n", + card->typ); + restore_flags(flags); + return (0); + } + if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for dlog(card %d)\n", + cardnr + 1); + restore_flags(flags); + return (0); + } + if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for status_buf(card %d)\n", + cardnr + 1); + kfree(cs->dlog); + restore_flags(flags); + return (0); + } + cs->stlist = NULL; + cs->mon_tx = NULL; + cs->mon_rx = NULL; + cs->status_read = cs->status_buf; + cs->status_write = cs->status_buf; + cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1; + cs->typ = card->typ; + strcpy(cs->iif.id, id); + cs->iif.channels = 2; + cs->iif.maxbufsize = MAX_DATA_SIZE; + cs->iif.hl_hdrlen = MAX_HEADER_LEN; + cs->iif.features = + ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_HDLC | +// ISDN_FEATURE_L2_MODEM | + ISDN_FEATURE_L2_TRANS | + ISDN_FEATURE_L3_TRANS | +#ifdef CONFIG_HISAX_1TR6 + ISDN_FEATURE_P_1TR6 | +#endif +#ifdef CONFIG_HISAX_EURO + ISDN_FEATURE_P_EURO | +#endif +#ifdef CONFIG_HISAX_NI1 + ISDN_FEATURE_P_NI1 | +#endif + 0; + + cs->iif.command = HiSax_command; + cs->iif.writecmd = NULL; + cs->iif.writebuf_skb = HiSax_writebuf_skb; + cs->iif.readstat = HiSax_readstatus; + register_isdn(&cs->iif); + cs->myid = cs->iif.channels; + printk(KERN_INFO + "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1, + (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" : + (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" : + (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" : + (card->protocol == ISDN_PTYPE_NI1) ? "NI1" : + "NONE", cs->iif.id, cs->myid); + switch (card->typ) { +#if CARD_TELES0 + case ISDN_CTYPE_16_0: + case ISDN_CTYPE_8_0: + ret = setup_teles0(card); + break; +#endif +#if CARD_TELES3 + case ISDN_CTYPE_16_3: + case ISDN_CTYPE_PNP: + case ISDN_CTYPE_TELESPCMCIA: + case ISDN_CTYPE_COMPAQ_ISA: + ret = setup_teles3(card); + break; +#endif +#if CARD_S0BOX + case ISDN_CTYPE_S0BOX: + ret = setup_s0box(card); + break; +#endif +#if CARD_TELESPCI + case ISDN_CTYPE_TELESPCI: + ret = setup_telespci(card); + break; +#endif +#if CARD_AVM_A1 + case ISDN_CTYPE_A1: + ret = setup_avm_a1(card); + break; +#endif +#if CARD_AVM_A1_PCMCIA + case ISDN_CTYPE_A1_PCMCIA: + ret = setup_avm_a1_pcmcia(card); + break; +#endif +#if CARD_FRITZPCI + case ISDN_CTYPE_FRITZPCI: + ret = setup_avm_pcipnp(card); + break; +#endif +#if CARD_ELSA + case ISDN_CTYPE_ELSA: + case ISDN_CTYPE_ELSA_PNP: + case ISDN_CTYPE_ELSA_PCMCIA: + case ISDN_CTYPE_ELSA_PCI: + ret = setup_elsa(card); + break; +#endif +#if CARD_IX1MICROR2 + case ISDN_CTYPE_IX1MICROR2: + ret = setup_ix1micro(card); + break; +#endif +#if CARD_DIEHLDIVA + case ISDN_CTYPE_DIEHLDIVA: + ret = setup_diva(card); + break; +#endif +#if CARD_ASUSCOM + case ISDN_CTYPE_ASUSCOM: + ret = setup_asuscom(card); + break; +#endif +#if CARD_TELEINT + case ISDN_CTYPE_TELEINT: + ret = setup_TeleInt(card); + break; +#endif +#if CARD_SEDLBAUER + case ISDN_CTYPE_SEDLBAUER: + case ISDN_CTYPE_SEDLBAUER_PCMCIA: + case ISDN_CTYPE_SEDLBAUER_FAX: + ret = setup_sedlbauer(card); + break; +#endif +#if CARD_SPORTSTER + case ISDN_CTYPE_SPORTSTER: + ret = setup_sportster(card); + break; +#endif +#if CARD_MIC + case ISDN_CTYPE_MIC: + ret = setup_mic(card); + break; +#endif +#if CARD_NETJET + case ISDN_CTYPE_NETJET: + ret = setup_netjet(card); + break; +#endif +#if CARD_TELES3C + case ISDN_CTYPE_TELES3C: + ret = setup_t163c(card); + break; +#endif +#if CARD_NICCY + case ISDN_CTYPE_NICCY: + ret = setup_niccy(card); + break; +#endif +#if CARD_AMD7930 + case ISDN_CTYPE_AMD7930: + ret = setup_amd7930(card); + break; +#endif + default: + printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n", + card->typ); + ll_unload(cs); + restore_flags(flags); + return (0); + } + if (!ret) { + ll_unload(cs); + restore_flags(flags); + return (0); + } + if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for isac rcvbuf\n"); + return (1); + } + cs->rcvidx = 0; + cs->tx_skb = NULL; + cs->tx_cnt = 0; + cs->event = 0; + cs->tqueue.next = 0; + cs->tqueue.sync = 0; + cs->tqueue.data = cs; + + skb_queue_head_init(&cs->rq); + skb_queue_head_init(&cs->sq); + + init_bcstate(cs, 0); + init_bcstate(cs, 1); + ret = init_card(cs); + if (ret) { + closecard(cardnr); + restore_flags(flags); + return (0); + } + init_tei(cs, cs->protocol); + CallcNewChan(cs); + /* ISAR needs firmware download first */ + if (!test_bit(HW_ISAR, &cs->HW_Flags)) + ll_run(cs); + restore_flags(flags); + return (1); +} + +HISAX_INITFUNC(void +HiSax_shiftcards(int idx)) { int i; - char tmp[64], rev[64]; - char *r = rev; + + for (i = idx; i < (HISAX_MAX_CARDS - 1); i++) + memcpy(&cards[i], &cards[i + 1], sizeof(cards[i])); +} + +HISAX_INITFUNC(int +HiSax_inithardware(int *busy_flag)) +{ + int foundcards = 0; + int i = 0; + int t = ','; + int flg = 0; + char *id; + char *next_id = HiSax_id; + char ids[20]; + + if (strchr(HiSax_id, ',')) + t = ','; + else if (strchr(HiSax_id, '%')) + t = '%'; + + while (i < nrcards) { + if (cards[i].typ < 1) + break; + id = next_id; + if ((next_id = strchr(id, t))) { + *next_id++ = 0; + strcpy(ids, id); + flg = i + 1; + } else { + next_id = id; + if (flg >= i) + strcpy(ids, id); + else + sprintf(ids, "%s%d", id, i); + } + if (checkcard(i, ids, busy_flag)) { + foundcards++; + i++; + } else { + printk(KERN_WARNING "HiSax: Card %s not installed !\n", + CardType[cards[i].typ]); + if (cards[i].cs) + kfree((void *) cards[i].cs); + cards[i].cs = NULL; + HiSax_shiftcards(i); + } + } + return foundcards; +} + +void +HiSax_closecard(int cardnr) +{ + int i,last=nrcards - 1; + + if (cardnr>last) + return; + if (cards[cardnr].cs) { + ll_stop(cards[cardnr].cs); + release_tei(cards[cardnr].cs); + closecard(cardnr); + free_irq(cards[cardnr].cs->irq, cards[cardnr].cs); + kfree((void *) cards[cardnr].cs); + cards[cardnr].cs = NULL; + } + i = cardnr; + while (i!=last) { + cards[i] = cards[i+1]; + i++; + } + nrcards--; +} + +void +HiSax_reportcard(int cardnr) +{ + struct IsdnCardState *cs = cards[cardnr].cs; + struct PStack *stptr; + struct l3_process *pc; + int j, i = 1; + + printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1); + printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]); + printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug); + printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n", + (ulong) & HiSax_reportcard); + printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs); + printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc0 flg %x\n", + cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag); + printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n", + cs->bcs[0].mode, cs->bcs[0].channel); + printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n", + cs->bcs[1].mode, cs->bcs[1].channel); + printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist)); + stptr = cs->stlist; + while (stptr != NULL) { + printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr); + printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp); + printk(KERN_DEBUG "HiSax: tei %d sapi %d\n", + stptr->l2.tei, stptr->l2.sap); + printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); + pc = stptr->l3.proc; + while (pc) { + printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref, + (ulong) pc); + printk(KERN_DEBUG "HiSax: state %d st 0x%lX chan 0x%lX\n", + pc->state, (ulong) pc->st, (ulong) pc->chan); + pc = pc->next; + } + stptr = stptr->next; + i++; + } + for (j = 0; j < 2; j++) { + printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j, + (ulong) & cs->channel[j]); + stptr = cs->channel[j].b_st; + i = 1; + while (stptr != NULL) { + printk(KERN_DEBUG "HiSax: b_st%d 0x%lX\n", i, (ulong) stptr); + printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer); + stptr = stptr->next; + i++; + } + } +} + + +__initfunc(int +HiSax_init(void)) +{ + int i; + #ifdef MODULE int nzproto = 0; +#ifdef CONFIG_HISAX_ELSA + if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) { + /* we have exported and return in this case */ + register_elsa_symbols(); + return 0; + } +#endif +#ifdef CONFIG_HISAX_SEDLBAUER + if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) { + /* we have to export and return in this case */ + register_symtab(&hisax_syms_sedl); + return 0; + } +#endif +#ifdef CONFIG_HISAX_AVM_A1_PCMCIA + if (type[0] == ISDN_CTYPE_A1_PCMCIA) { + /* we have to export and return in this case */ + register_avm_a1_symbols(); + return 0; + } +#endif #endif nrcards = 0; - strcpy(tmp, l1_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l2_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l3_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, l4_revision); - r += sprintf(r, "%s/", HiSax_getrev(tmp)); - strcpy(tmp, tei_revision); - r += sprintf(r, "%s", HiSax_getrev(tmp)); - - printk(KERN_NOTICE "HiSax: Driver for Siemens chip set ISDN cards\n"); - printk(KERN_NOTICE "HiSax: Version 2.1\n"); - printk(KERN_NOTICE "HiSax: Revisions %s\n", rev); - + HiSaxVersion(); #ifdef MODULE if (id) /* If id= string used */ HiSax_id = id; - for (i = 0; i < 16; i++) { + for (i = 0; i < HISAX_MAX_CARDS; i++) { cards[i].typ = type[i]; if (protocol[i]) { cards[i].protocol = protocol[i]; @@ -343,37 +1306,49 @@ cards[i].para[1] = mem[i]; break; - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_TELESPCMCIA: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; - break; - -#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */ +#ifdef IO0_IO1 case ISDN_CTYPE_PNP: + case ISDN_CTYPE_NICCY: cards[i].para[0] = irq[i]; cards[i].para[1] = io0[i]; cards[i].para[2] = io1[i]; break; -#endif - case ISDN_CTYPE_A1: + case ISDN_CTYPE_COMPAQ_ISA: cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; + cards[i].para[1] = io0[i]; + cards[i].para[2] = io1[i]; + cards[i].para[3] = io[i]; break; - +#endif case ISDN_CTYPE_ELSA: cards[i].para[0] = io[i]; break; - case ISDN_CTYPE_ELSA_QS1000: - cards[i].para[0] = irq[i]; - cards[i].para[1] = io[i]; - break; - + case ISDN_CTYPE_16_3: + case ISDN_CTYPE_TELESPCMCIA: + case ISDN_CTYPE_A1: + case ISDN_CTYPE_A1_PCMCIA: + case ISDN_CTYPE_ELSA_PNP: + case ISDN_CTYPE_ELSA_PCMCIA: case ISDN_CTYPE_IX1MICROR2: + case ISDN_CTYPE_DIEHLDIVA: + case ISDN_CTYPE_ASUSCOM: + case ISDN_CTYPE_TELEINT: + case ISDN_CTYPE_SEDLBAUER: + case ISDN_CTYPE_SEDLBAUER_PCMCIA: + case ISDN_CTYPE_SEDLBAUER_FAX: + case ISDN_CTYPE_SPORTSTER: + case ISDN_CTYPE_MIC: + case ISDN_CTYPE_TELES3C: + case ISDN_CTYPE_S0BOX: + case ISDN_CTYPE_FRITZPCI: cards[i].para[0] = irq[i]; cards[i].para[1] = io[i]; break; - + case ISDN_CTYPE_ELSA_PCI: + case ISDN_CTYPE_NETJET: + case ISDN_CTYPE_AMD7930: + case ISDN_CTYPE_TELESPCI: + break; } } if (!nzproto) { @@ -386,29 +1361,31 @@ HiSax_id = HiSaxID; if (!HiSaxID[0]) strcpy(HiSaxID, "HiSax"); - for (i = 0; i < 16; i++) + for (i = 0; i < HISAX_MAX_CARDS; i++) if (cards[i].typ > 0) nrcards++; printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", nrcards, (nrcards > 1) ? "s" : ""); CallcNew(); + Isdnl3New(); Isdnl2New(); - if (HiSax_inithardware()) { + TeiNew(); + Isdnl1New(); + if (HiSax_inithardware(NULL)) { /* Install only, if at least one card found */ /* No symbols to export, hide all symbols */ #ifdef MODULE -#if (LINUX_VERSION_CODE < 0x020111) register_symtab(NULL); -#else - EXPORT_NO_SYMBOLS; -#endif - printk(KERN_NOTICE "HiSax: module installed\n"); + printk(KERN_INFO "HiSax: module installed\n"); #endif return (0); } else { + Isdnl1Free(); + TeiFree(); Isdnl2Free(); + Isdnl3Free(); CallcFree(); return -EIO; } @@ -418,8 +1395,172 @@ void cleanup_module(void) { - HiSax_closehardware(); - printk(KERN_NOTICE "HiSax module removed\n"); + int cardnr = nrcards -1; + long flags; + + save_flags(flags); + cli(); + while(cardnr>=0) + HiSax_closecard(cardnr--); + Isdnl1Free(); + TeiFree(); + Isdnl2Free(); + Isdnl3Free(); + CallcFree(); + restore_flags(flags); + printk(KERN_INFO "HiSax module removed\n"); } +#endif + +#ifdef CONFIG_HISAX_ELSA +int elsa_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) +{ +#ifdef MODULE + int i; + int nzproto = 0; + nrcards = 0; + HiSaxVersion(); + if (id) /* If id= string used */ + HiSax_id = id; + /* Initialize all 8 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < HISAX_MAX_CARDS; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = 10; + nzproto = 1; + + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < HISAX_MAX_CARDS; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl3New(); + Isdnl2New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); +#endif + return (0); +} +#endif + +#ifdef CONFIG_HISAX_SEDLBAUER +int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) +{ +#ifdef MODULE + int i; + int nzproto = 0; + + nrcards = 0; + HiSaxVersion(); + if (id) /* If id= string used */ + HiSax_id = id; + /* Initialize all 8 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < HISAX_MAX_CARDS; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA; + nzproto = 1; + + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < HISAX_MAX_CARDS; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + CallcNew(); + Isdnl3New(); + Isdnl2New(); + Isdnl1New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); +#endif + return (0); +} +#endif + +#ifdef CONFIG_HISAX_AVM_A1_PCMCIA +int avm_a1_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot) +{ +#ifdef MODULE + int i; + int nzproto = 0; + + nrcards = 0; + HiSaxVersion(); + if (id) /* If id= string used */ + HiSax_id = id; + /* Initialize all 16 structs, even though we only accept + two pcmcia cards + */ + for (i = 0; i < 16; i++) { + cards[i].para[0] = irq[i]; + cards[i].para[1] = io[i]; + cards[i].typ = type[i]; + if (protocol[i]) { + cards[i].protocol = protocol[i]; + nzproto++; + } + } + cards[0].para[0] = pcm_irq; + cards[0].para[1] = (int)pcm_iob; + cards[0].protocol = prot; + cards[0].typ = ISDN_CTYPE_A1_PCMCIA; + nzproto = 1; + + if (!HiSax_id) + HiSax_id = HiSaxID; + if (!HiSaxID[0]) + strcpy(HiSaxID, "HiSax"); + for (i = 0; i < HISAX_MAX_CARDS; i++) + if (cards[i].typ > 0) + nrcards++; + printk(KERN_DEBUG "HiSax: Total %d card%s defined\n", + nrcards, (nrcards > 1) ? "s" : ""); + + Isdnl1New(); + CallcNew(); + Isdnl3New(); + Isdnl2New(); + TeiNew(); + HiSax_inithardware(busy_flag); + printk(KERN_NOTICE "HiSax: module installed\n"); +#endif + return (0); +} #endif diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/diva.c linux/drivers/isdn/hisax/diva.c --- v2.0.35/linux/drivers/isdn/hisax/diva.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/diva.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,613 @@ +/* $Id: diva.c,v 1.1.2.11 1998/11/05 21:13:51 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 + * + * + * $Log: diva.c,v $ + * Revision 1.1.2.11 1998/11/05 21:13:51 keil + * minor fixes + * + * Revision 1.1.2.10 1998/11/03 00:06:10 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.9 1998/06/27 21:58:34 keil + * fix release of diva 2.01 + * + * Revision 1.1.2.8 1998/05/27 18:05:11 keil + * HiSax 3.0 + * + * Revision 1.1.2.7 1998/04/24 13:37:13 keil + * Support for DIVA 2.01 IPAC + * + * Revision 1.1.2.6 1998/04/08 22:05:21 keil + * Forgot PCI fix + * + * Revision 1.1.2.5 1998/04/08 21:49:27 keil + * New init; fix PCI for more as one card + * + * Revision 1.1.2.4 1998/03/07 23:15:14 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.1.2.3 1998/01/27 22:37:35 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:44 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:10:36 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/18 17:11:20 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "ipac.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; + +const char *Diva_revision = "$Revision: 1.1.2.11 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define DIVA_HSCX_DATA 0 +#define DIVA_HSCX_ADR 4 +#define DIVA_ISA_ISAC_DATA 2 +#define DIVA_ISA_ISAC_ADR 6 +#define DIVA_ISA_CTRL 7 +#define DIVA_IPAC_ADR 0 +#define DIVA_IPAC_DATA 1 + +#define DIVA_PCI_ISAC_DATA 8 +#define DIVA_PCI_ISAC_ADR 0xc +#define DIVA_PCI_CTRL 0x10 + +/* SUB Types */ +#define DIVA_ISA 1 +#define DIVA_PCI 2 +#define DIVA_IPAC_ISA 3 + +/* PCI stuff */ +#define PCI_VENDOR_EICON_DIEHL 0x1133 +#define PCI_DIVA20PRO_ID 0xe001 +#define PCI_DIVA20_ID 0xe002 +#define PCI_DIVA20PRO_U_ID 0xe003 +#define PCI_DIVA20_U_ID 0xe004 + +/* CTRL (Read) */ +#define DIVA_IRQ_STAT 0x01 +#define DIVA_EEPROM_SDA 0x02 +/* CTRL (Write) */ +#define DIVA_IRQ_REQ 0x01 +#define DIVA_RESET 0x08 +#define DIVA_EEPROM_CLK 0x40 +#define DIVA_PCI_LED_A 0x10 +#define DIVA_PCI_LED_B 0x20 +#define DIVA_ISA_LED_A 0x20 +#define DIVA_ISA_LED_B 0x40 +#define DIVA_IRQ_CLR 0x80 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size); +} + +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset+0x80)); +} + +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset|0x80, value); +} + +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size); +} + +static void +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return(readreg(cs->hw.diva.hscx_adr, + cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.diva.hscx_adr, + cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \ + cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +diva_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval, stat = 0; + int cnt=8; + + if (!cs) { + printk(KERN_WARNING "Diva: Spurious interrupt!\n"); + return; + } + while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) { + val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40); + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + cnt--; + } + if (!cnt) + printk(KERN_WARNING "Diva: IRQ LOOP\n"); + if (stat & 1) { + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0); + } +} + +static void +diva_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista,val; + int icnt=20; + + if (!cs) { + printk(KERN_WARNING "Diva: Spurious interrupt!\n"); + return; + } + ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); +Start_IPAC: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + hscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n"); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0); +} + + +void +release_io_diva(struct IsdnCardState *cs) +{ + int bytecnt; + + if (cs->subtyp != DIVA_IPAC_ISA) { + del_timer(&cs->hw.diva.tl); + if (cs->hw.diva.cfg_reg) + byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */ + } + if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA)) + bytecnt = 8; + else + bytecnt = 32; + if (cs->hw.diva.cfg_reg) { + release_region(cs->hw.diva.cfg_reg, bytecnt); + } +} + +static void +reset_diva(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + if (cs->subtyp == DIVA_IPAC_ISA) { + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0); + schedule(); + } else { + cs->hw.diva.ctrl_reg = 0; /* Reset On */ + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */ + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + if (cs->subtyp == DIVA_ISA) + cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A; + else + cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A; + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + } + restore_flags(flags); +} + +#define DIVA_ASSIGN 1 + +static void +diva_led_handler(struct IsdnCardState *cs) +{ + int blink = 0; + + if (cs->subtyp == DIVA_IPAC_ISA) + return; + del_timer(&cs->hw.diva.tl); + if (cs->hw.diva.status & DIVA_ASSIGN) + cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_A : DIVA_PCI_LED_A; + else { + cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_A : DIVA_PCI_LED_A; + blink = 250; + } + if (cs->hw.diva.status & 0xf000) + cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B; + else if (cs->hw.diva.status & 0x0f00) { + cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B; + blink = 500; + } else + cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ? + DIVA_ISA_LED_B : DIVA_PCI_LED_B); + + byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); + if (blink) { + init_timer(&cs->hw.diva.tl); + cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000); + add_timer(&cs->hw.diva.tl); + } +} + +static int +Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + u_int irq_flag = I4L_IRQ_FLAG; + + switch (mt) { + case CARD_RESET: + reset_diva(cs); + return(0); + case CARD_RELEASE: + release_io_diva(cs); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == DIVA_PCI) + irq_flag |= SA_SHIRQ; + if (cs->subtyp == DIVA_IPAC_ISA) { + return(request_irq(cs->irq, &diva_interrupt_ipac, + irq_flag, "HiSax", cs)); + } else { + return(request_irq(cs->irq, &diva_interrupt, + irq_flag, "HiSax", cs)); + } + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + case (MDL_REMOVE | REQUEST): + cs->hw.diva.status = 0; + break; + case (MDL_ASSIGN | REQUEST): + cs->hw.diva.status |= DIVA_ASSIGN; + break; + case MDL_INFO_SETUP: + if ((long)arg) + cs->hw.diva.status |= 0x0200; + else + cs->hw.diva.status |= 0x0100; + break; + case MDL_INFO_CONN: + if ((long)arg) + cs->hw.diva.status |= 0x2000; + else + cs->hw.diva.status |= 0x1000; + break; + case MDL_INFO_REL: + if ((long)arg) { + cs->hw.diva.status &= ~0x2000; + cs->hw.diva.status &= ~0x0200; + } else { + cs->hw.diva.status &= ~0x1000; + cs->hw.diva.status &= ~0x0100; + } + break; + } + if (cs->subtyp != DIVA_IPAC_ISA) + diva_led_handler(cs); + return(0); +} + + + +static int pci_index __initdata = 0; + +__initfunc(int +setup_diva(struct IsdnCard *card)) +{ + int bytecnt; + u_char val; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Diva_revision); + printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_DIEHLDIVA) + return(0); + cs->hw.diva.status = 0; + if (card->para[1]) { + cs->hw.diva.ctrl_reg = 0; + cs->hw.diva.cfg_reg = card->para[1]; + 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) { + cs->subtyp = DIVA_IPAC_ISA; + cs->hw.diva.ctrl = 0; + cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA; + cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA; + cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR; + cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + } else { + cs->subtyp = DIVA_ISA; + cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL; + cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA; + cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR; + cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR; + } + cs->irq = card->para[0]; + bytecnt = 8; + } else { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL, + PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = DIVA_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Diva: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Diva: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.diva.cfg_reg = pci_ioaddr; + cs->hw.diva.ctrl = pci_ioaddr + DIVA_PCI_CTRL; + cs->hw.diva.isac = pci_ioaddr + DIVA_PCI_ISAC_DATA; + cs->hw.diva.hscx = pci_ioaddr + DIVA_HSCX_DATA; + cs->hw.diva.isac_adr = pci_ioaddr + DIVA_PCI_ISAC_ADR; + cs->hw.diva.hscx_adr = pci_ioaddr + DIVA_HSCX_ADR; + cs->irq = pci_irq; + bytecnt = 32; +#else + printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Diva: unable to config DIVA PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } + + printk(KERN_INFO + "Diva: %s card configured at 0x%x IRQ %d\n", + (cs->subtyp == DIVA_PCI) ? "PCI" : + (cs->subtyp == DIVA_ISA) ? "ISA" : "IPAC", + cs->hw.diva.cfg_reg, cs->irq); + if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.diva.cfg_reg, + cs->hw.diva.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn"); + } + + reset_diva(cs); + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Diva_card_msg; + if (cs->subtyp == DIVA_IPAC_ISA) { + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID); + printk(KERN_INFO "Diva: IPAC version %x\n", val); + } else { + cs->hw.diva.tl.function = (void *) diva_led_handler; + cs->hw.diva.tl.data = (long) cs; + init_timer(&cs->hw.diva.tl); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + ISACVersion(cs, "Diva:"); + if (HscxVersion(cs, "Diva:")) { + printk(KERN_WARNING + "Diva: wrong HSCX versions check IO address\n"); + release_io_diva(cs); + return (0); + } + } + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c --- v2.0.35/linux/drivers/isdn/hisax/elsa.c Tue Aug 5 09:00:12 1997 +++ linux/drivers/isdn/hisax/elsa.c Sun Nov 15 10:32:58 1998 @@ -1,1248 +1,876 @@ -/* $Id: elsa.c,v 1.14 1997/04/13 19:53:25 keil Exp $ +/* $Id: elsa.c,v 1.14.2.16 1998/11/05 21:13:55 keil Exp $ * elsa.c low level stuff for Elsa isdn cards * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert * * Thanks to Elsa GmbH for documents and informations * + * Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE) + * for ELSA PCMCIA support + * * * $Log: elsa.c,v $ - * Revision 1.14 1997/04/13 19:53:25 keil - * Fixed QS1000 init, change in IRQ check delay for SMP + * Revision 1.14.2.16 1998/11/05 21:13:55 keil + * minor fixes * - * Revision 1.13 1997/04/07 22:58:07 keil - * need include config.h + * Revision 1.14.2.15 1998/11/03 00:06:14 keil + * certification related changes + * fixed logging for smaller stack use * - * Revision 1.12 1997/04/06 22:54:14 keil - * Using SKB's + * Revision 1.14.2.14 1998/10/25 19:43:58 fritz + * Removed a compiler warning + * + * Revision 1.14.2.13 1998/10/25 17:47:48 fritz + * Line power status only valid for ISA cards. + * + * Revision 1.14.2.12 1998/09/27 13:05:53 keil + * Apply most changes from 2.1.X (HiSax 3.1) * - * Revision 1.11 1997/03/23 21:45:46 keil - * Add support for ELSA PCMCIA + * Revision 1.14.2.11 1998/05/27 18:05:14 keil + * HiSax 3.0 * - * Revision 1.10 1997/03/12 21:42:19 keil - * Bugfix: IRQ hangs with QS1000 + * Revision 1.14.2.10 1998/04/11 18:46:03 keil + * QS3000PCI support, changes for arcofi * - * Revision 1.9 1997/03/04 15:57:39 keil - * bugfix IRQ reset Quickstep, ELSA PC changes, some stuff for new cards + * Revision 1.14.2.9 1998/04/08 21:44:36 keil + * new init; fix PCI for more as one card * - * Revision 1.8 1997/01/27 15:51:48 keil - * SMP proof,cosmetics + * Revision 1.14.2.8 1998/03/07 23:15:15 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.7 1997/01/21 22:20:48 keil - * Elsa Quickstep support + * Revision 1.14.2.7 1998/01/27 22:37:36 keil + * fast io * - * Revision 1.6 1997/01/09 18:22:46 keil - * one more PCC-8 fix + * Revision 1.14.2.6 1998/01/11 22:57:10 keil + * add PCMCIA maintainer Klaus * - * Revision 1.5 1996/12/08 19:46:14 keil - * PCC-8 correct IRQs; starting ARCOFI support + * Revision 1.14.2.5 1997/11/15 18:50:47 keil + * new common init function * - * Revision 1.4 1996/11/18 20:50:54 keil - * with PCF Pro release 16 Byte IO + * Revision 1.14.2.4 1997/10/17 22:13:44 keil + * update to last hisax version * - * Revision 1.3 1996/11/18 15:33:04 keil - * PCC and PCFPro support + * Revision 2.1 1997/07/27 21:47:08 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:40 keil + * New Layer and card interface + * + * Revision 1.14 1997/04/13 19:53:25 keil + * Fixed QS1000 init, change in IRQ check delay for SMP * - * Revision 1.2 1996/10/27 22:08:03 keil - * cosmetic changes + * Revision 1.13 1997/04/07 22:58:07 keil + * need include config.h * - * Revision 1.1 1996/10/13 20:04:52 keil - * Initial revision + * Revision 1.12 1997/04/06 22:54:14 keil + * Using SKB's * + * old changes removed KKe * */ -#define ARCOFI_USE 0 - #define __NO_VERSION__ #include -#include "siemens.h" #include "hisax.h" -#include "elsa.h" +#include "arcofi.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" #include "isdnl1.h" -#include +#include +#include +#include +#include extern const char *CardType[]; -const char *Elsa_revision = "$Revision: 1.14 $"; +const char *Elsa_revision = "$Revision: 1.14.2.16 $"; const char *Elsa_Types[] = {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro", - "PCMCIA", "QS 1000", "QS 3000"}; + "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"}; const char *ITACVer[] = {"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2", "B1", "A1"}; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) - -static inline u_char -readhscx(unsigned int adr, int hscx, u_char off) -{ - register u_char ret; - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20)); - ret = bytein(adr + CARD_HSCX); - restore_flags(flags); - return (ret); -} - -static inline void -read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) -{ - /* fifo read without cli because it's allready done */ - - byteout(adr + CARD_ALE, (hscx ? 0x40 : 0)); - insb(adr + CARD_HSCX, data, size); -} +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) +#define ELSA_ISAC 0 +#define ELSA_ISAC_PCM 1 +#define ELSA_ITAC 1 +#define ELSA_HSCX 2 +#define ELSA_ALE 3 +#define ELSA_ALE_PCM 4 +#define ELSA_CONTROL 4 +#define ELSA_CONFIG 5 +#define ELSA_START_TIMER 6 +#define ELSA_TRIG_IRQ 7 + +#define ELSA_PC 1 +#define ELSA_PCC8 2 +#define ELSA_PCC16 3 +#define ELSA_PCF 4 +#define ELSA_PCFPRO 5 +#define ELSA_PCMCIA 6 +#define ELSA_QS1000 7 +#define ELSA_QS3000 8 +#define ELSA_QS1000PCI 9 +#define ELSA_QS3000PCI 10 + +/* PCI stuff */ +#define PCI_VENDOR_ELSA 0x1048 +#define PCI_QS1000_ID 0x1000 +#define PCI_QS3000_ID 0x3000 +#define ELSA_PCI_IRQ_MASK 0x04 + +/* ITAC Registeradressen (only Microlink PC) */ +#define ITAC_SYS 0x34 +#define ITAC_ISEN 0x48 +#define ITAC_RFIE 0x4A +#define ITAC_XFIE 0x4C +#define ITAC_SCIE 0x4E +#define ITAC_STIE 0x46 + +/*** *** + *** Makros als Befehle fuer die Kartenregister *** + *** (mehrere Befehle werden durch Bit-Oderung kombiniert) *** + *** ***/ + +/* Config-Register (Read) */ +#define ELSA_TIMER_RUN 0x02 /* Bit 1 des Config-Reg */ +#define ELSA_TIMER_RUN_PCC8 0x01 /* Bit 0 des Config-Reg bei PCC */ +#define ELSA_IRQ_IDX 0x38 /* Bit 3,4,5 des Config-Reg */ +#define ELSA_IRQ_IDX_PCC8 0x30 /* Bit 4,5 des Config-Reg */ +#define ELSA_IRQ_IDX_PC 0x0c /* Bit 2,3 des Config-Reg */ + +/* Control-Register (Write) */ +#define ELSA_LINE_LED 0x02 /* Bit 1 Gelbe LED */ +#define ELSA_STAT_LED 0x08 /* Bit 3 Gruene LED */ +#define ELSA_ISDN_RESET 0x20 /* Bit 5 Reset-Leitung */ +#define ELSA_ENA_TIMER_INT 0x80 /* Bit 7 Freigabe Timer Interrupt */ + +/* ALE-Register (Read) */ +#define ELSA_HW_RELEASE 0x07 /* Bit 0-2 Hardwarerkennung */ +#define ELSA_S0_POWER_BAD 0x08 /* Bit 3 S0-Bus Spannung fehlt */ + +/* Status Flags */ +#define ELSA_TIMER_AKTIV 1 +#define ELSA_BAD_PWR 2 +#define ELSA_ASSIGN 4 + +#define RS_ISR_PASS_LIMIT 256 +#define _INLINE_ inline +#define FLG_MODEM_ACTIVE 1 +/* IPAC AUX */ +#define ELSA_IPAC_LINE_LED 0x40 /* Bit 6 Gelbe LED */ +#define ELSA_IPAC_STAT_LED 0x80 /* Bit 7 Gruene LED */ + +const u_char ARCOFI_VERSION[] = {2,0xa0,0}; +const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */ +const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */ +const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */ +const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */ +const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}; /* RX */ +const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */ +const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR UP */ +const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* Normal OP */ +const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}; -static inline void -writehscx(unsigned int adr, int hscx, u_char off, u_char data) -{ - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20)); - byteout(adr + CARD_HSCX, data); - restore_flags(flags); -} +static void set_arcofi(struct IsdnCardState *cs, int bc); -static inline void -write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) -{ - /* fifo write without cli because it's allready done */ - byteout(adr + CARD_ALE, (hscx ? 0x40 : 0)); - outsb(adr + CARD_HSCX, data, size); -} +#if ARCOFI_USE +#include "elsa_ser.c" +#endif static inline u_char -readisac(unsigned int adr, u_char off) +readreg(unsigned int ale, unsigned int adr, u_char off) { register u_char ret; long flags; save_flags(flags); cli(); - byteout(adr + CARD_ALE, off + 0x20); - ret = bytein(adr + CARD_ISAC); + byteout(ale, off); + ret = bytein(adr); restore_flags(flags); return (ret); } static inline void -read_fifo_isac(unsigned int adr, u_char * data, int size) +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { /* fifo read without cli because it's allready done */ - byteout(adr + CARD_ALE, 0); - insb(adr + CARD_ISAC, data, size); + byteout(ale, off); + insb(adr, data, size); } static inline void -writeisac(unsigned int adr, u_char off, u_char data) +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) { long flags; save_flags(flags); cli(); - byteout(adr + CARD_ALE, off + 0x20); - byteout(adr + CARD_ISAC, data); + byteout(ale, off); + byteout(adr, data); restore_flags(flags); } static inline void -write_fifo_isac(unsigned int adr, u_char * data, int size) +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { /* fifo write without cli because it's allready done */ - - byteout(adr + CARD_ALE, 0); - outsb(adr + CARD_ISAC, data, size); + byteout(ale, off); + outsb(adr, data, size); } -#ifdef CONFIG_HISAX_ELSA_PCC -static inline u_char -readitac(unsigned int adr, u_char off) -{ - register u_char ret; - long flags; +/* Interface functions */ - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off); - ret = bytein(adr + CARD_ITAC); - restore_flags(flags); - return (ret); +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset)); } -static inline void -writeitac(unsigned int adr, u_char off, u_char data) +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - long flags; - - save_flags(flags); - cli(); - byteout(adr + CARD_ALE, off); - byteout(adr + CARD_ITAC, data); - restore_flags(flags); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value); } -static inline int -TimerRun(struct IsdnCardState *sp) +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - register u_char val; - - val = bytein(sp->cfg_reg + CARD_CONFIG); - if (sp->subtyp == ELSA_QS1000) - return (0 == (val & TIMER_RUN)); - else if (sp->subtyp == ELSA_PCC8) - return (val & TIMER_RUN_PCC8); - return (val & TIMER_RUN); + readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size); } -static inline void -elsa_led_handler(struct IsdnCardState *sp) +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - - u_char outval = 0xf0; - int stat = 0, cval; - - - if ((sp->ph_state == 0) || (sp->ph_state == 15)) { - stat = 1; - } else { - if (sp->hs[0].mode != 0) - stat |= 2; - if (sp->hs[1].mode != 0) - stat |= 4; - } - cval = (sp->counter >> 6) & 3; - switch (cval) { - case 0: - if (!stat) - outval |= STAT_LED; - else if (stat == 1) - outval |= LINE_LED | STAT_LED; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 1: - if (!stat) - outval |= LINE_LED; - else if (stat == 1) - outval |= LINE_LED | STAT_LED; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 2: - if (!stat) - outval |= STAT_LED; - else if (stat == 1) - outval |= 0; - else { - if (stat & 2) - outval |= STAT_LED; - if (stat & 4) - outval |= LINE_LED; - } - break; - case 3: - if (!stat) - outval |= LINE_LED; - break; - } - byteout(sp->cfg_reg + CARD_CONTROL, outval); + writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size); } -#endif -static inline void -waitforCEC(int adr, int hscx) +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) { - int to = 50; - - while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Elsa: waitforCEC timeout\n"); + return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80)); } - -static inline void -waitforXFW(int adr, int hscx) +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) { - int to = 50; - - while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Elsa: waitforXFW timeout\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value); } -static inline void -writehscxCMDR(int adr, int hscx, u_char data) +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr, hscx); - writehscx(adr, hscx, HSCX_CMDR, data); - restore_flags(flags); + readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size); } -/* - * fast interrupt here - */ - - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->cfg_reg, hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_EXIR)); + writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size); } -void -elsa_report(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readisac(sp->cfg_reg, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readisac(sp->cfg_reg, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readisac(sp->cfg_reg, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + return (readreg(cs->hw.elsa.ale, + cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0))); } -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.elsa.ale, + cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value); } -static void -hscx_fill_fifo(struct HscxState *hsp) +static inline u_char +readitac(struct IsdnCardState *cs, u_char off) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; + register u_char ret; long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->cfg_reg, hsp->hscx); save_flags(flags); cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count); - writehscxCMDR(sp->cfg_reg, hsp->hscx, more ? 0x8 : 0xa); + byteout(cs->hw.elsa.ale, off); + ret = bytein(cs->hw.elsa.itac); restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (ret); } static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) -{ - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!(r & 0x80)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!(r & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80); - } else { - count = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (sp->debug & L1_DEB_HSCX_FIFO) { - sprintf(tmp, "HX Frame %d", count); - debugl1(sp, tmp); - } - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "Elsa: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "elsa: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } -} - -/* - * ISAC stuff goes here - */ - -static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +writeitac(struct IsdnCardState *cs, u_char off, u_char data) { - u_char *ptr; long flags; - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; save_flags(flags); cli(); - read_fifo_isac(sp->cfg_reg, ptr, count); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); + byteout(cs->hw.elsa.ale, off); + byteout(cs->hw.elsa.itac, data); restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static inline int +TimerRun(struct IsdnCardState *cs) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; + register u_char v; - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo_isac(sp->cfg_reg, ptr, count); - writeisac(sp->cfg_reg, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + v = bytein(cs->hw.elsa.cfg); + if ((cs->subtyp == ELSA_QS1000) || (cs->subtyp == ELSA_QS3000)) + return (0 == (v & ELSA_TIMER_RUN)); + else if (cs->subtyp == ELSA_PCC8) + return (v & ELSA_TIMER_RUN_PCC8); + return (v & ELSA_TIMER_RUN); } +/* + * fast interrupt HSCX stuff goes here + */ -static void -ph_command(struct IsdnCardState *sp, unsigned int command) -{ - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writeisac(sp->cfg_reg, ISAC_CIX0, (command << 2) | 3); -} +#define READHSCX(cs, nr, reg) readreg(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0), data) -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval, v1; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; -#if ARCOFI_USE - struct BufHeader *ibh; - u_char *ptr; -#endif +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt) - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readisac(sp->cfg_reg, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x80); - } else { - count = readisac(sp->cfg_reg, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - sp->rcvidx = 0; - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "Elsa: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readisac(sp->cfg_reg, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readisac(sp->cfg_reg, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - if (exval & 0x08) { - v1 = readisac(sp->cfg_reg, ISAC_MOSR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOSR %02x", v1); - debugl1(sp, tmp); - } -#if ARCOFI_USE - if (v1 & 0x08) { - if (!sp->mon_rx) - if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX out of buffers!"); - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - goto afterMONR0; - } else - sp->mon_rxp = 0; - ibh = sp->mon_rx; - ptr = DATAPTR(ibh); - ptr += sp->mon_rxp; - sp->mon_rxp++; - if (sp->mon_rxp >= 3072) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - sp->mon_rxp = 0; - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX overflow!"); - goto afterMONR0; - } - *ptr = readisac(sp->cfg_reg, ISAC_MOR0); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOR0 %02x", *ptr); - debugl1(sp, tmp); - } - } - afterMONR0: - if (v1 & 0x80) { - if (!sp->mon_rx) - if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX out of buffers!"); - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - goto afterMONR1; - } else - sp->mon_rxp = 0; - ibh = sp->mon_rx; - ptr = DATAPTR(ibh); - ptr += sp->mon_rxp; - sp->mon_rxp++; - if (sp->mon_rxp >= 3072) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - sp->mon_rxp = 0; - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON RX overflow!"); - goto afterMONR1; - } - *ptr = readisac(sp->cfg_reg, ISAC_MOR1); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC MOR1 %02x", *ptr); - debugl1(sp, tmp); - } - } - afterMONR1: - if (v1 & 0x04) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - sp->mon_rx->datasize = sp->mon_rxp; - sp->mon_flg |= MON0_RX; - } - if (v1 & 0x40) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - sp->mon_rx->datasize = sp->mon_rxp; - sp->mon_flg |= MON1_RX; - } - if (v1 == 0x02) { - ibh = sp->mon_tx; - if (!ibh) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a); - goto AfterMOX0; - } - count = ibh->datasize - sp->mon_txp; - if (count <= 0) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0x0f); - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sp->mon_flg |= MON0_TX; - goto AfterMOX0; - } - ptr = DATAPTR(ibh); - ptr += sp->mon_txp; - sp->mon_txp++; - writeisac(sp->cfg_reg, ISAC_MOX0, *ptr); - } - AfterMOX0: - if (v1 == 0x20) { - ibh = sp->mon_tx; - if (!ibh) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - goto AfterMOX1; - } - count = ibh->datasize - sp->mon_txp; - if (count <= 0) { - writeisac(sp->cfg_reg, ISAC_MOCR, 0xf0); - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sp->mon_flg |= MON1_TX; - goto AfterMOX1; - } - ptr = DATAPTR(ibh); - ptr += sp->mon_txp; - sp->mon_txp++; - writeisac(sp->cfg_reg, ISAC_MOX1, *ptr); - } - AfterMOX1: -#endif - } - } -} +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \ + cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt) -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ - - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readhscx(sp->cfg_reg, 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readhscx(sp->cfg_reg, 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readhscx(sp->cfg_reg, 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val; + int icnt=20; - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); return; } -#ifdef CONFIG_HISAX_ELSA_PCC - INT_RESTART: - if (!TimerRun(sp)) { - /* Timer Restart */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - if (!(sp->counter++ & 0x3f)) { - /* Call LEDs all 64 tics */ - elsa_led_handler(sp); + if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) { + /* The card tends to generate interrupts while being removed + causing us to just crash the kernel. bad. */ + printk(KERN_WARNING "Elsa: card not available!\n"); + return; + } +#if ARCOFI_USE + if (cs->hw.elsa.MFlag) { + val = serial_inp(cs, UART_IIR); + if (!(val & UART_IIR_NO_INT)) { + debugl1(cs,"IIR %02x", val); + rs_interrupt_elsa(intno, cs); } } #endif - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); } - val = readisac(sp->cfg_reg, ISAC_ISTA); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); } -#ifdef CONFIG_HISAX_ELSA_PCC - if (!TimerRun(sp)) - goto INT_RESTART; -#endif - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); + if (val && icnt) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + icnt--; goto Start_HSCX; } - val = readisac(sp->cfg_reg, ISAC_ISTA); - if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA); + if (val && icnt) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + icnt--; goto Start_ISAC; } - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF); - writeisac(sp->cfg_reg, ISAC_MASK, 0xFF); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_QS1000) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); + if (!icnt) + printk(KERN_WARNING"ELSA IRQ LOOP\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0xFF); + if (cs->hw.elsa.status & ELSA_TIMER_AKTIV) { + if (!TimerRun(cs)) { + /* Timer Restart */ + byteout(cs->hw.elsa.timer, 0); + cs->hw.elsa.counter++; + } } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_MASK, 0x0); + if (cs->hw.elsa.MFlag) { + val = serial_inp(cs, UART_MCR); + val ^= 0x8; + serial_outp(cs, UART_MCR, val); + val = serial_inp(cs, UART_MCR); + val ^= 0x8; + serial_outp(cs, UART_MCR, val); + } + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0x00); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0); } - static void -initisac(struct IsdnCardState *sp) +elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) { - unsigned int adr = sp->cfg_reg; + struct IsdnCardState *cs = dev_id; + u_char ista,val; + int icnt=20; - /* Elsa IOM 2 Mode */ - writeisac(adr, ISAC_MASK, 0xff); - writeisac(adr, ISAC_ADF2, 0x80); - writeisac(adr, ISAC_SQXR, 0x2f); - writeisac(adr, ISAC_SPCR, 0x00); - writeisac(adr, ISAC_STCR, 0x70); - writeisac(adr, ISAC_MODE, 0xc9); - writeisac(adr, ISAC_TIMR, 0x00); - writeisac(adr, ISAC_ADF1, 0x00); - writeisac(adr, ISAC_CIX0, (1 << 2) | 3); - writeisac(adr, ISAC_MASK, 0xff); - writeisac(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writehscx(sp->cfg_reg, hscx, HSCX_CCR1, 0x85); - writehscx(sp->cfg_reg, hscx, HSCX_XAD1, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_XAD2, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_RAH2, 0xFF); - writehscx(sp->cfg_reg, hscx, HSCX_XBCH, 0x0); - writehscx(sp->cfg_reg, hscx, HSCX_RLCR, 0x0); - writehscx(sp->cfg_reg, hscx, HSCX_CCR2, 0x30); - - switch (mode) { - case (0): - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0xff); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0xff); - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f); - } else { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3); - } - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0xe4); - writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f); - } else { - writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3); - writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3); - } - writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7); - writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x8c); - writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41); - break; + if (!cs) { + printk(KERN_WARNING "Elsa: Spurious interrupt!\n"); + return; } - writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00); + 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); + if (!(val & UART_IIR_NO_INT)) { + debugl1(cs,"IIR %02x", val); + rs_interrupt_elsa(intno, cs); + } + } +#endif + ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA); +Start_IPAC: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + hscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "ELSA IRQ LOOP\n"); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xFF); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xC0); } void -release_io_elsa(struct IsdnCard *card) +release_io_elsa(struct IsdnCardState *cs) { int bytecnt = 8; - if (card->sp->subtyp == ELSA_PCFPRO) + del_timer(&cs->hw.elsa.tl); + if (cs->hw.elsa.ctrl) + byteout(cs->hw.elsa.ctrl, 0); /* LEDs Out */ + if (cs->subtyp == ELSA_QS1000PCI) { + byteout(cs->hw.elsa.cfg + 0x4c, 0x01); /* disable IRQ */ + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); + bytecnt = 2; + release_region(cs->hw.elsa.cfg, 0x80); + } + if (cs->subtyp == ELSA_QS3000PCI) { + byteout(cs->hw.elsa.cfg + 0x4c, 0x03); /* disable ELSA PCI IRQ */ + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff); + release_region(cs->hw.elsa.cfg, 0x80); + } + if ((cs->subtyp == ELSA_PCFPRO) || + (cs->subtyp == ELSA_QS3000) || + (cs->subtyp == ELSA_PCF) || + (cs->subtyp == ELSA_QS3000PCI)) { bytecnt = 16; - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, bytecnt); -} - -static void -reset_elsa(struct IsdnCardState *sp) -{ -#ifdef CONFIG_HISAX_ELSA_PCC - /* Wait 1 Timer */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - while (TimerRun(sp)); - byteout(sp->cfg_reg + CARD_CONTROL, 0x00); /* Reset On */ - /* Wait 1 Timer */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - while (TimerRun(sp)); - byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET); /* Reset Off */ - /* Wait 1 Timer */ - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - while (TimerRun(sp)); - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); -#endif + release_modem(cs); + } + if (cs->hw.elsa.base) + release_region(cs->hw.elsa.base, bytecnt); } static void -clear_pending_ints(struct IsdnCardState *sp) +reset_elsa(struct IsdnCardState *cs) { -#ifdef CONFIG_HISAX_ELSA_PCMCIA - int val; - char tmp[64]; + long flags; - val = readhscx(sp->cfg_reg, 1, HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readhscx(sp->cfg_reg, 1, HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readhscx(sp->cfg_reg, 0, HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readhscx(sp->cfg_reg, 0, HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readhscx(sp->cfg_reg, 1, HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readhscx(sp->cfg_reg, 0, HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readisac(sp->cfg_reg, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readisac(sp->cfg_reg, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readisac(sp->cfg_reg, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); + if (cs->hw.elsa.timer) { + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + cs->hw.elsa.ctrl_reg |= 0x50; + cs->hw.elsa.ctrl_reg &= ~ELSA_ISDN_RESET; /* Reset On */ + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + cs->hw.elsa.ctrl_reg |= ELSA_ISDN_RESET; /* Reset Off */ + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + /* Wait 1 Timer */ + byteout(cs->hw.elsa.timer, 0); + while (TimerRun(cs)); + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0xff); } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF); - writeisac(sp->cfg_reg, ISAC_MASK, 0xFF); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_QS1000) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + save_flags(flags); + sti(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0); + schedule(); + 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); + 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 */ + else if (cs->subtyp == ELSA_QS3000PCI) + byteout(cs->hw.elsa.cfg + 0x4c, 0x43); /* enable ELSA PCI IRQ */ } -#endif - writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0); - writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_MASK, 0x0); - writeisac(sp->cfg_reg, ISAC_CMDR, 0x41); } static void -check_arcofi(struct IsdnCardState *sp) -{ -#if 0 - u_char val; - char tmp[40]; - char *t; +init_arcofi(struct IsdnCardState *cs) { + send_arcofi(cs, ARCOFI_XOP_0, 1, 0); +/* send_arcofi(cs, ARCOFI_XOP_F, 1); +*/ +} + +#define ARCDEL 500 + +static void +set_arcofi(struct IsdnCardState *cs, int bc) { long flags; - u_char *p; - if (BufPoolGet(&(sp->mon_tx), &(sp->sbufpool), - GFP_ATOMIC, (void *) 1, 3)) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC MON TX out of buffers!"); - return; - } else - sp->mon_txp = 0; - p = DATAPTR(sp->mon_tx); - *p++ = 0xa0; - *p++ = 0x0; - sp->mon_tx->datasize = 2; - sp->mon_txp = 1; - sp->mon_flg = 0; - writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0); - val = readisac(sp->cfg_reg, ISAC_MOSR); - writeisac(sp->cfg_reg, ISAC_MOX1, 0xa0); - writeisac(sp->cfg_reg, ISAC_MOCR, 0xb0); + debugl1(cs,"set_arcofi bc=%d", bc); save_flags(flags); sti(); - HZDELAY(3); + send_arcofi(cs, ARCOFI_XOP_0, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_5, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_6, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_7, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_8, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_COP_9, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_SOP_F, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_XOP_1, bc, 0); + udelay(ARCDEL); + send_arcofi(cs, ARCOFI_XOP_F, bc, 0); restore_flags(flags); - if (sp->mon_flg & MON1_TX) { - if (sp->mon_flg & MON1_RX) { - sprintf(tmp, "Arcofi response received %d bytes", sp->mon_rx->datasize); - debugl1(sp, tmp); - p = DATAPTR(sp->mon_rx); + debugl1(cs,"end set_arcofi bc=%d", bc); +} + +static int +check_arcofi(struct IsdnCardState *cs) +{ +#if ARCOFI_USE + int arcofi_present = 0; + char tmp[40]; + char *t; + u_char *p; + + if (!cs->mon_tx) + if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON TX out of buffers!"); + return(0); + } + send_arcofi(cs, ARCOFI_VERSION, 0, 1); + if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) { + if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) { + debugl1(cs, "Arcofi response received %d bytes", cs->mon_rxp); + p = cs->mon_rx; t = tmp; t += sprintf(tmp, "Arcofi data"); - QuickHex(t, p, sp->mon_rx->datasize); - debugl1(sp, tmp); - BufPoolRelease(sp->mon_rx); - sp->mon_rx = NULL; - sp->mon_rxp = 0; - sp->mon_flg = 0; + QuickHex(t, p, cs->mon_rxp); + debugl1(cs, tmp); + if ((cs->mon_rxp == 2) && (cs->mon_rx[0] == 0xa0)) { + switch(cs->mon_rx[1]) { + case 0x80: + debugl1(cs, "Arcofi 2160 detected"); + arcofi_present = 1; + break; + case 0x82: + debugl1(cs, "Arcofi 2165 detected"); + arcofi_present = 2; + break; + case 0x84: + debugl1(cs, "Arcofi 2163 detected"); + arcofi_present = 3; + break; + default: + debugl1(cs, "unknown Arcofi response"); + break; + } + } else + debugl1(cs, "undefined Monitor response"); + cs->mon_rxp = 0; } - } else if (sp->mon_tx) { - BufPoolRelease(sp->mon_tx); - sp->mon_tx = NULL; - sp->mon_txp = 0; - sprintf(tmp, "Arcofi not detected"); - debugl1(sp, tmp); + } else if (cs->mon_tx) { + debugl1(cs, "Arcofi not detected"); + } + if (arcofi_present) { + if (cs->subtyp==ELSA_QS1000) { + cs->subtyp = ELSA_QS3000; + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + release_region(cs->hw.elsa.base, 8); + if (check_region(cs->hw.elsa.base, 16)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base + 8, + cs->hw.elsa.base + 16); + } else + request_region(cs->hw.elsa.base, 16, + "elsa isdn modem"); + } else if (cs->subtyp==ELSA_PCC16) { + cs->subtyp = ELSA_PCF; + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + release_region(cs->hw.elsa.base, 8); + if (check_region(cs->hw.elsa.base, 16)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base + 8, + cs->hw.elsa.base + 16); + } else + request_region(cs->hw.elsa.base, 16, + "elsa isdn modem"); + } else + printk(KERN_INFO + "Elsa: %s detected modem at 0x%x\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base+8); + init_arcofi(cs); + return(1); } - sp->mon_flg = 0; #endif + return(0); } -int -initelsa(struct IsdnCardState *sp) +static void +elsa_led_handler(struct IsdnCardState *cs) +{ + int blink = 0; + + if (cs->subtyp == ELSA_PCMCIA) + return; + del_timer(&cs->hw.elsa.tl); + if (cs->hw.elsa.status & ELSA_ASSIGN) + cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED; + else if (cs->hw.elsa.status & ELSA_BAD_PWR) + cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED; + else { + cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED; + blink = 250; + } + if (cs->hw.elsa.status & 0xf000) + cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED; + else if (cs->hw.elsa.status & 0x0f00) { + cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED; + blink = 500; + } else + cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED; + + if ((cs->subtyp == ELSA_QS1000PCI) || + (cs->subtyp == ELSA_QS3000PCI)) { + u_char led = 0xff; + if (cs->hw.elsa.ctrl_reg & ELSA_LINE_LED) + led ^= ELSA_IPAC_LINE_LED; + if (cs->hw.elsa.ctrl_reg & ELSA_STAT_LED) + led ^= ELSA_IPAC_STAT_LED; + writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, led); + } else + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + if (blink) { + init_timer(&cs->hw.elsa.tl); + cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000); + add_timer(&cs->hw.elsa.tl); + } +} + +static int +Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int ret, irq_cnt, cnt = 3; + int len, ret = 0; + u_char *msg; long flags; - irq_cnt = kstat.interrupts[sp->irq]; - printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, irq_cnt); - ret = get_irq(sp->cardnr, &elsa_interrupt); -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); -#endif - while (ret && cnt) { - sp->counter = 0; - clear_pending_ints(sp); - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - save_flags(flags); - sp->counter = 0; - sti(); -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET | ENABLE_TIM_INT); - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (110 * HZ) / 1000; /* Timeout 110ms */ - schedule(); - restore_flags(flags); - printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n", - sp->counter); - if (abs(sp->counter - 13) < 3) { - printk(KERN_INFO "Elsa: timer and irq OK\n"); - } else { - printk(KERN_WARNING - "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n", - sp->counter, sp->irq); - } + switch (mt) { + case CARD_RESET: + reset_elsa(cs); + return(0); + case CARD_RELEASE: + release_io_elsa(cs); + return(0); + case CARD_SETIRQ: + if ((cs->subtyp == ELSA_QS1000PCI) || + (cs->subtyp == ELSA_QS3000PCI)) + ret = request_irq(cs->irq, &elsa_interrupt_ipac, + I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs); + else + ret = request_irq(cs->irq, &elsa_interrupt, + I4L_IRQ_FLAG, "HiSax", cs); + return(ret); + case CARD_INIT: + cs->debug |= L1_DEB_IPAC; + inithscxisac(cs, 1); + if ((cs->subtyp == ELSA_QS1000) || + (cs->subtyp == ELSA_QS3000)) + { + byteout(cs->hw.elsa.timer, 0); + } + if (cs->hw.elsa.trig) + byteout(cs->hw.elsa.trig, 0xff); + inithscxisac(cs, 2); + return(0); + case CARD_TEST: + if ((cs->subtyp == ELSA_PCMCIA) || + (cs->subtyp == ELSA_QS1000PCI)) { + return(0); + } else if (cs->subtyp == ELSA_QS3000PCI) { + ret = 0; + } else { + save_flags(flags); + cs->hw.elsa.counter = 0; + sti(); + cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT; + cs->hw.elsa.status |= ELSA_TIMER_AKTIV; + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + byteout(cs->hw.elsa.timer, 0); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (110 * HZ) / 1000; /* Timeout 110ms */ + schedule(); + restore_flags(flags); + cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT; + byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg); + cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV; + printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n", + cs->hw.elsa.counter); + if (abs(cs->hw.elsa.counter - 13) < 3) { + printk(KERN_INFO "Elsa: timer and irq OK\n"); + ret = 0; + } else { + printk(KERN_WARNING + "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n", + cs->hw.elsa.counter, cs->irq); + ret = 1; + } + } +#if ARCOFI_USE + if (check_arcofi(cs)) { + init_modem(cs); + } #endif - printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, - kstat.interrupts[sp->irq]); - if (kstat.interrupts[sp->irq] == irq_cnt) { - printk(KERN_WARNING - "Elsa: IRQ(%d) getting no interrupts during init %d\n", - sp->irq, 4 - cnt); - if (cnt == 1) { - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); + elsa_led_handler(cs); + return(ret); + case (MDL_REMOVE | REQUEST): + cs->hw.elsa.status &= 0; + break; + case (MDL_ASSIGN | REQUEST): + cs->hw.elsa.status |= ELSA_ASSIGN; + break; + case MDL_INFO_SETUP: + if ((long) arg) + cs->hw.elsa.status |= 0x0200; + else + cs->hw.elsa.status |= 0x0100; + break; + case MDL_INFO_CONN: + if ((long) arg) + cs->hw.elsa.status |= 0x2000; + else + cs->hw.elsa.status |= 0x1000; + break; + case MDL_INFO_REL: + if ((long) arg) { + cs->hw.elsa.status &= ~0x2000; + cs->hw.elsa.status &= ~0x0200; } else { - reset_elsa(sp); - cnt--; + cs->hw.elsa.status &= ~0x1000; + cs->hw.elsa.status &= ~0x0100; } - } else { - check_arcofi(sp); - cnt = 0; - } + break; + case CARD_AUX_IND: + if (cs->hw.elsa.MFlag) { + if (!arg) + return(0); + msg = arg; + len = *msg; + msg++; + modem_write_cmd(cs, msg, len); + } + break; } - sp->counter = 0; - return (ret); + if (cs->typ == ISDN_CTYPE_ELSA) { + int pwr = bytein(cs->hw.elsa.ale); + if (pwr & 0x08) + cs->hw.elsa.status |= ELSA_BAD_PWR; + else + cs->hw.elsa.status &= ~ELSA_BAD_PWR; + } + elsa_led_handler(cs); + return(ret); } -#ifdef CONFIG_HISAX_ELSA_PCC static unsigned char -probe_elsa_adr(unsigned int adr) +probe_elsa_adr(unsigned int adr, int typ) { int i, in1, in2, p16_1 = 0, p16_2 = 0, p8_1 = 0, p8_2 = 0, pc_1 = 0, pc_2 = 0, pfp_1 = 0, pfp_2 = 0; long flags; - if (check_region(adr, 8)) { + /* In case of the elsa pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(adr, 8)) { printk(KERN_WARNING "Elsa: Probing Port 0x%x: already in use\n", adr); @@ -1251,8 +879,8 @@ save_flags(flags); cli(); for (i = 0; i < 16; i++) { - in1 = inb(adr + CARD_CONFIG); /* 'toggelt' bei */ - in2 = inb(adr + CARD_CONFIG); /* jedem Zugriff */ + in1 = inb(adr + ELSA_CONFIG); /* 'toggelt' bei */ + in2 = inb(adr + ELSA_CONFIG); /* jedem Zugriff */ p16_1 += 0x04 & in1; p16_2 += 0x04 & in2; p8_1 += 0x02 & in1; @@ -1283,205 +911,301 @@ } static unsigned int -probe_elsa(struct IsdnCardState *sp) +probe_elsa(struct IsdnCardState *cs) { int i; unsigned int CARD_portlist[] = {0x160, 0x170, 0x260, 0x360, 0}; for (i = 0; CARD_portlist[i]; i++) { - if ((sp->subtyp = probe_elsa_adr(CARD_portlist[i]))) + if ((cs->subtyp = probe_elsa_adr(CARD_portlist[i], cs->typ))) break; } return (CARD_portlist[i]); } -#endif + +static int pci_index __initdata = 0; int setup_elsa(struct IsdnCard *card) { -#ifdef CONFIG_HISAX_ELSA_PCC long flags; -#endif int bytecnt; - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, Elsa_revision); - printk(KERN_NOTICE "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp)); -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->typ == ISDN_CTYPE_ELSA) { - sp->cfg_reg = card->para[0]; + printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp)); + cs->hw.elsa.ctrl_reg = 0; + cs->hw.elsa.status = 0; + cs->hw.elsa.MFlag = 0; + if (cs->typ == ISDN_CTYPE_ELSA) { + cs->hw.elsa.base = card->para[0]; printk(KERN_INFO "Elsa: Microlink IO probing\n"); - if (sp->cfg_reg) { - if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) { + if (cs->hw.elsa.base) { + if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base, + cs->typ))) { printk(KERN_WARNING "Elsa: no Elsa Microlink at 0x%x\n", - sp->cfg_reg); + cs->hw.elsa.base); return (0); } } else - sp->cfg_reg = probe_elsa(sp); - if (sp->cfg_reg) { - val = bytein(sp->cfg_reg + CARD_CONFIG); - if (sp->subtyp == ELSA_PC) { + cs->hw.elsa.base = probe_elsa(cs); + if (cs->hw.elsa.base) { + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + val = bytein(cs->hw.elsa.cfg); + if (cs->subtyp == ELSA_PC) { const u_char CARD_IrqTab[8] = {7, 3, 5, 9, 0, 0, 0, 0}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PC) >> 2]; - } else if (sp->subtyp == ELSA_PCC8) { + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2]; + } else if (cs->subtyp == ELSA_PCC8) { const u_char CARD_IrqTab[8] = {7, 3, 5, 9, 0, 0, 0, 0}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX_PCC8) >> 4]; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4]; } else { const u_char CARD_IrqTab[8] = {15, 10, 15, 3, 11, 5, 11, 9}; - sp->irq = CARD_IrqTab[(val & IRQ_INDEX) >> 3]; + cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3]; } - val = bytein(sp->cfg_reg + CARD_ALE) & 0x7; + val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE; if (val < 3) val |= 8; val += 'A' - 3; if (val == 'B' || val == 'C') val ^= 1; - if ((sp->subtyp == ELSA_PCFPRO) && (val = 'G')) + if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G')) val = 'C'; printk(KERN_INFO "Elsa: %s found at 0x%x Rev.:%c IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - val, sp->irq); - val = bytein(sp->cfg_reg + CARD_ALE) & 0x08; - if (val) + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + val, cs->irq); + val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD; + if (val) { printk(KERN_WARNING "Elsa: Microlink S0 bus power bad\n"); + cs->hw.elsa.status |= ELSA_BAD_PWR; + } } else { printk(KERN_WARNING "No Elsa Microlink found\n"); return (0); } - } else if (sp->typ == ISDN_CTYPE_ELSA_QS1000) { - sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - sp->subtyp = ELSA_QS1000; + } else if (cs->typ == ISDN_CTYPE_ELSA_PNP) { + cs->hw.elsa.base = card->para[1]; + cs->irq = card->para[0]; + cs->subtyp = ELSA_QS1000; + cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG; + cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE; + cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC; + cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX; + cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ; + cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER; + cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL; printk(KERN_INFO - "Elsa: %s found at 0x%x IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - sp->irq); - } else - return (0); -#endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA - if (sp->typ == ISDN_CTYPE_ELSA_QS1000) { - sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - sp->subtyp = ELSA_PCMCIA; + "Elsa: %s defined at 0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); + } 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; + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; + cs->hw.elsa.ctrl = 0; + printk(KERN_INFO + "Elsa: %s defined at 0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->irq); + } else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_ELSA, + PCI_QS1000_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = ELSA_QS1000PCI; + else if (pcibios_find_device(PCI_VENDOR_ELSA, + PCI_QS3000_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = ELSA_QS3000PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.cfg = pci_ioaddr; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_3, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Elsa: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n"); + return(0); + } + + if (!pci_ioaddr) { + printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.elsa.base = pci_ioaddr; + cs->hw.elsa.ale = pci_ioaddr; + cs->hw.elsa.isac = pci_ioaddr +1; + cs->hw.elsa.hscx = pci_ioaddr +1; + cs->irq = pci_irq; + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->hw.elsa.timer = 0; + cs->hw.elsa.trig = 0; printk(KERN_INFO - "Elsa: %s found at 0x%x IRQ %d\n", - Elsa_Types[sp->subtyp], - sp->cfg_reg, - sp->irq); + "Elsa: %s defined at 0x%x/0x%x IRQ %d\n", + Elsa_Types[cs->subtyp], + cs->hw.elsa.base, + cs->hw.elsa.cfg, + cs->irq); +#else + printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n"); + return (0); +#endif /* CONFIG_PCI */ } else return (0); -#endif - switch (sp->subtyp) { + switch (cs->subtyp) { case ELSA_PC: - bytecnt = 8; - break; case ELSA_PCC8: - bytecnt = 8; - break; - case ELSA_PCFPRO: - bytecnt = 16; - break; case ELSA_PCC16: + case ELSA_QS1000: + case ELSA_PCMCIA: bytecnt = 8; break; + case ELSA_PCFPRO: case ELSA_PCF: + case ELSA_QS3000PCI: bytecnt = 16; break; - case ELSA_QS1000: - bytecnt = 8; - break; - case ELSA_PCMCIA: - bytecnt = 8; + case ELSA_QS1000PCI: + bytecnt = 2; break; default: printk(KERN_WARNING - "Unknown ELSA subtype %d\n", sp->subtyp); + "Unknown ELSA subtype %d\n", cs->subtyp); return (0); } - - if (check_region((sp->cfg_reg), bytecnt)) { + /* In case of the elsa pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(cs->hw.elsa.base, bytecnt)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + bytecnt); + cs->hw.elsa.base, + cs->hw.elsa.base + bytecnt); return (0); } else { - request_region(sp->cfg_reg, bytecnt, "elsa isdn"); + request_region(cs->hw.elsa.base, bytecnt, "elsa isdn"); } - - /* Teste Timer */ -#ifdef CONFIG_HISAX_ELSA_PCC - byteout(sp->cfg_reg + CARD_TRIG_IRQ, 0xff); - byteout(sp->cfg_reg + CARD_START_TIMER, 0); - if (!TimerRun(sp)) { - byteout(sp->cfg_reg + CARD_START_TIMER, 0); /* 2. Versuch */ - if (!TimerRun(sp)) { + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + if (check_region(cs->hw.elsa.cfg, 0x80)) { printk(KERN_WARNING - "Elsa: timer do not start\n"); - release_io_elsa(card); + "HiSax: %s pci port %x-%x already in use\n", + CardType[card->typ], + cs->hw.elsa.cfg, + cs->hw.elsa.cfg + 0x80); + release_region(cs->hw.elsa.base, bytecnt); return (0); + } else { + request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci"); } } - save_flags(flags); - sti(); - HZDELAY(1); /* wait >=10 ms */ - restore_flags(flags); - if (TimerRun(sp)) { - printk(KERN_WARNING "Elsa: timer do not run down\n"); - release_io_elsa(card); - return (0); + cs->hw.elsa.tl.function = (void *) elsa_led_handler; + cs->hw.elsa.tl.data = (long) cs; + init_timer(&cs->hw.elsa.tl); + /* Teste Timer */ + if (cs->hw.elsa.timer) { + byteout(cs->hw.elsa.trig, 0xff); + byteout(cs->hw.elsa.timer, 0); + if (!TimerRun(cs)) { + byteout(cs->hw.elsa.timer, 0); /* 2. Versuch */ + if (!TimerRun(cs)) { + printk(KERN_WARNING + "Elsa: timer do not start\n"); + release_io_elsa(cs); + return (0); + } + } + save_flags(flags); + sti(); + HZDELAY(1); /* wait >=10 ms */ + restore_flags(flags); + if (TimerRun(cs)) { + printk(KERN_WARNING "Elsa: timer do not run down\n"); + release_io_elsa(cs); + return (0); + } + printk(KERN_INFO "Elsa: timer OK; resetting card\n"); } - printk(KERN_INFO "Elsa: timer OK; resetting card\n"); - reset_elsa(sp); -#endif - verA = readhscx(sp->cfg_reg, 0, HSCX_VSTR) & 0xf; - verB = readhscx(sp->cfg_reg, 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "Elsa: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readisac(sp->cfg_reg, ISAC_RBCH); - printk(KERN_INFO "Elsa: ISAC %s\n", - ISACVersion(val)); - -#ifdef CONFIG_HISAX_ELSA_PCMCIA - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { - printk(KERN_WARNING - "Elsa: wrong HSCX versions check IO address\n"); - release_io_elsa(card); - return (0); + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Elsa_card_msg; + reset_elsa(cs); + if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) { + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID); + printk(KERN_INFO "Elsa: IPAC version %x\n", val); + } else { + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + ISACVersion(cs, "Elsa:"); + if (HscxVersion(cs, "Elsa:")) { + printk(KERN_WARNING + "Elsa: wrong HSCX versions check IO address\n"); + release_io_elsa(cs); + return (0); + } } -#endif - -#ifdef CONFIG_HISAX_ELSA_PCC - if (sp->subtyp == ELSA_PC) { - val = readitac(sp->cfg_reg, ITAC_SYS); + if (cs->subtyp == ELSA_PC) { + val = readitac(cs, ITAC_SYS); printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]); - writeitac(sp->cfg_reg, ITAC_ISEN, 0); - writeitac(sp->cfg_reg, ITAC_RFIE, 0); - writeitac(sp->cfg_reg, ITAC_XFIE, 0); - writeitac(sp->cfg_reg, ITAC_SCIE, 0); - writeitac(sp->cfg_reg, ITAC_STIE, 0); + writeitac(cs, ITAC_ISEN, 0); + writeitac(cs, ITAC_RFIE, 0); + writeitac(cs, ITAC_XFIE, 0); + writeitac(cs, ITAC_SCIE, 0); + writeitac(cs, ITAC_STIE, 0); } -#endif - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; - return (1); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/elsa_ser.c linux/drivers/isdn/hisax/elsa_ser.c --- v2.0.35/linux/drivers/isdn/hisax/elsa_ser.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/elsa_ser.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,748 @@ +#include +#include + +#define MAX_MODEM_BUF 256 +#define WAKEUP_CHARS (MAX_MODEM_BUF/2) +#define RS_ISR_PASS_LIMIT 256 +#define BASE_BAUD ( 1843200 / 16 ) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +//#define SERIAL_DEBUG_OPEN 1 +//#define SERIAL_DEBUG_INTR 1 +//#define SERIAL_DEBUG_FLOW 1 +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_REG +//#define SERIAL_DEBUG_REG 1 + +#ifdef SERIAL_DEBUG_REG +static u_char deb[32]; +const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"}; +const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"}; +#endif + +static char *MInit_1 = "AT&F&C1E0&D2\r\0"; +static char *MInit_2 = "ATL2M1S64=13\r\0"; +static char *MInit_3 = "AT+FCLASS=0\r\0"; +static char *MInit_4 = "ATV1S2=128X1\r\0"; +static char *MInit_5 = "AT\\V8\\N3\r\0"; +static char *MInit_6 = "ATL0M0&G0%E1\r\0"; +static char *MInit_7 = "AT%L1%M0%C3\r\0"; + +static char *MInit_speed28800 = "AT%G0%B28800\r\0"; + +static char *MInit_dialout = "ATs7=60 x1 d\r\0"; +static char *MInit_dialin = "ATs7=60 x1 a\r\0"; + + +static inline unsigned int serial_in(struct IsdnCardState *cs, int offset) +{ +#ifdef SERIAL_DEBUG_REG + u_int val = inb(cs->hw.elsa.base + 8 + offset); + debugl1(cs,"in %s %02x",ModemIn[offset], val); + return(val); +#else + return inb(cs->hw.elsa.base + 8 + offset); +#endif +} + +static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset) +{ +#ifdef SERIAL_DEBUG_REG +#ifdef CONFIG_SERIAL_NOPAUSE_IO + u_int val = inb(cs->hw.elsa.base + 8 + offset); + debugl1(cs,"inp %s %02x",ModemIn[offset], val); +#else + u_int val = inb_p(cs->hw.elsa.base + 8 + offset); + debugl1(cs,"inP %s %02x",ModemIn[offset], val); +#endif + return(val); +#else +#ifdef CONFIG_SERIAL_NOPAUSE_IO + return inb(cs->hw.elsa.base + 8 + offset); +#else + return inb_p(cs->hw.elsa.base + 8 + offset); +#endif +#endif +} + +static inline void serial_out(struct IsdnCardState *cs, int offset, int value) +{ +#ifdef SERIAL_DEBUG_REG + debugl1(cs,"out %s %02x",ModemOut[offset], value); +#endif + outb(value, cs->hw.elsa.base + 8 + offset); +} + +static inline void serial_outp(struct IsdnCardState *cs, int offset, + int value) +{ +#ifdef SERIAL_DEBUG_REG +#ifdef CONFIG_SERIAL_NOPAUSE_IO + debugl1(cs,"outp %s %02x",ModemOut[offset], value); +#else + debugl1(cs,"outP %s %02x",ModemOut[offset], value); +#endif +#endif +#ifdef CONFIG_SERIAL_NOPAUSE_IO + outb(value, cs->hw.elsa.base + 8 + offset); +#else + outb_p(value, cs->hw.elsa.base + 8 + offset); +#endif +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(struct IsdnCardState *cs, int baud) +{ + int quot = 0, baud_base; + unsigned cval, fcr = 0; + int bits; + unsigned long flags; + + + /* byte size and parity */ + cval = 0x03; bits = 10; + /* Determine divisor based on baud rate */ + baud_base = BASE_BAUD; + quot = baud_base / baud; + /* If the quotient is ever zero, default to 9600 bps */ + if (!quot) + quot = baud_base / 9600; + + /* Set up FIFO's */ + if ((baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + serial_outp(cs, UART_FCR, fcr); + /* CTS flow control flag and modem status interrupts */ + cs->hw.elsa.IER &= ~UART_IER_MSI; + cs->hw.elsa.IER |= UART_IER_MSI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); + + debugl1(cs,"modem quot=0x%x", quot); + save_flags(flags); + cli(); + serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + serial_outp(cs, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(cs, UART_DLM, quot >> 8); /* MS of divisor */ + serial_outp(cs, UART_LCR, cval); /* reset DLAB */ + serial_inp(cs, UART_RX); + restore_flags(flags); +} + +static int mstartup(struct IsdnCardState *cs) +{ + unsigned long flags; + int retval=0; + + + save_flags(flags); cli(); + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); + + /* + * At this point there's no way the LSR could still be 0xFF; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (serial_inp(cs, UART_LSR) == 0xff) { + retval = -ENODEV; + goto errout; + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(cs, UART_RX); + (void) serial_inp(cs, UART_IIR); + (void) serial_inp(cs, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_outp(cs, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + + cs->hw.elsa.MCR = 0; + cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; + serial_outp(cs, UART_MCR, cs->hw.elsa.MCR); + + /* + * Finally, enable interrupts + */ + cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); /* enable interrupts */ + + /* + * And clear the interrupt registers again for luck. + */ + (void)serial_inp(cs, UART_LSR); + (void)serial_inp(cs, UART_RX); + (void)serial_inp(cs, UART_IIR); + (void)serial_inp(cs, UART_MSR); + + cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0; + cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0; + + /* + * and set the speed of the serial port + */ + change_speed(cs, BASE_BAUD); + cs->hw.elsa.MFlag = 1; +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void mshutdown(struct IsdnCardState *cs) +{ + unsigned long flags; + + +#ifdef SERIAL_DEBUG_OPEN + printk(KERN_DEBUG"Shutting down serial ...."); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + + cs->hw.elsa.IER = 0; + serial_outp(cs, UART_IER, 0x00); /* disable all intrs */ + cs->hw.elsa.MCR &= ~UART_MCR_OUT2; + + /* disable break condition */ + serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC); + + cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(cs, UART_MCR, cs->hw.elsa.MCR); + + /* disable FIFO's */ + serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); + serial_inp(cs, UART_RX); /* read data port to reset things */ + + restore_flags(flags); +#ifdef SERIAL_DEBUG_OPEN + printk(" done\n"); +#endif +} + +inline int +write_modem(struct BCState *bcs) { + int ret=0; + struct IsdnCardState *cs = bcs->cs; + int count, len, fp, buflen; + long flags; + + if (!bcs->tx_skb) + return 0; + if (bcs->tx_skb->len <= 0) + return 0; + save_flags(flags); + cli(); + buflen = MAX_MODEM_BUF - cs->hw.elsa.transcnt; + len = MIN(buflen, bcs->tx_skb->len); + fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp; + fp &= (MAX_MODEM_BUF -1); + count = MIN(len, MAX_MODEM_BUF - fp); + if (count < len) { + memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count); + skb_pull(bcs->tx_skb, count); + cs->hw.elsa.transcnt += count; + ret = count; + count = len - count; + fp = 0; + } + memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count); + skb_pull(bcs->tx_skb, count); + cs->hw.elsa.transcnt += count; + ret += count; + + if (cs->hw.elsa.transcnt && + !(cs->hw.elsa.IER & UART_IER_THRI)) { + cs->hw.elsa.IER |= UART_IER_THRI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); + } + restore_flags(flags); + return(ret); +} + +inline void +modem_fill(struct BCState *bcs) { + + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + write_modem(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, + bcs->hw.hscx.count); + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + write_modem(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_sched_event(bcs, B_XMTBUFREADY); + } +} + +static inline void receive_chars(struct IsdnCardState *cs, + int *status) +{ + unsigned char ch; + struct sk_buff *skb; + + do { + ch = serial_in(cs, UART_RX); + if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF) + break; + cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch; +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + +#ifdef SERIAL_DEBUG_INTR + printk("handling exept...."); +#endif + } + *status = serial_inp(cs, UART_LSR); + } while (*status & UART_LSR_DR); + if (cs->hw.elsa.MFlag == 2) { + if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt))) + printk(KERN_WARNING "ElsaSER: receive out of memory\n"); + else { + memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf, + cs->hw.elsa.rcvcnt); + skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb); + } + hscx_sched_event(cs->hw.elsa.bcs, B_RCVBUFREADY); + } else { + char tmp[128]; + char *t = tmp; + + t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt); + QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt); + debugl1(cs, tmp); + } + cs->hw.elsa.rcvcnt = 0; +} + +static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done) +{ + int count; + + debugl1(cs, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp, + cs->hw.elsa.transcnt); + + if (cs->hw.elsa.transcnt <= 0) { + cs->hw.elsa.IER &= ~UART_IER_THRI; + serial_out(cs, UART_IER, cs->hw.elsa.IER); + return; + } + count = 16; + do { + serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]); + if (cs->hw.elsa.transp >= MAX_MODEM_BUF) + cs->hw.elsa.transp=0; + if (--cs->hw.elsa.transcnt <= 0) + break; + } while (--count > 0); + if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2)) + modem_fill(cs->hw.elsa.bcs); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (intr_done) + *intr_done = 0; + if (cs->hw.elsa.transcnt <= 0) { + cs->hw.elsa.IER &= ~UART_IER_THRI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); + } +} + +#if 0 +static inline void check_modem_status(struct IsdnCardState *cs) +{ + int status; + struct async_struct *info = cs->hw.elsa.info; + struct async_icount *icount; + + status = serial_inp(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + /* update input line counters */ + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; + } + if (status & UART_MSR_DCTS) + icount->cts++; +// wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttys%d CD now %s...", info->line, + (status & UART_MSR_DCD) ? "on" : "off"); +#endif + if (status & UART_MSR_DCD) +// wake_up_interruptible(&info->open_wait); +; + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SERIAL_DEBUG_OPEN + printk("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } +#if 0 + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + serial_outp(info, UART_IER, info->IER); +// rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & UART_MSR_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + serial_outp(info, UART_IER, info->IER); + } + } + } +#endif 0 +} +#endif + +static void rs_interrupt_elsa(int irq, struct IsdnCardState *cs) +{ + int status, iir, msr; + int pass_counter = 0; + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + do { + status = serial_inp(cs, UART_LSR); + debugl1(cs,"rs LSR %02x", status); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(cs, &status); + if (status & UART_LSR_THRE) + transmit_chars(cs, 0); + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("rs_single loop break.\n"); + break; + } + iir = serial_inp(cs, UART_IIR); + debugl1(cs,"rs IIR %02x", iir); + if ((iir & 0xf) == 0) { + msr = serial_inp(cs, UART_MSR); + debugl1(cs,"rs MSR %02x", msr); + } + } while (!(iir & UART_IIR_NO_INT)); +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +extern int open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs); +extern void modehscx(struct BCState *bcs, int mode, int bc); +extern void hscx_l2l1(struct PStack *st, int pr, void *arg); + +void +close_elsastate(struct BCState *bcs) +{ + struct sk_buff *skb; + + modehscx(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.hscx.rcvbuf) { + if (bcs->mode != L1_MODE_MODEM) + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + } + while ((skb = skb_dequeue(&bcs->rqueue))) { + dev_kfree_skb(skb, FREE_READ); + } + while ((skb = skb_dequeue(&bcs->squeue))) { + dev_kfree_skb(skb, FREE_WRITE); + } + if (bcs->tx_skb) { + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +void +modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) { + int count, fp; + u_char *msg = buf; + long flags; + + if (!len) + return; + save_flags(flags); + cli(); + if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) { + restore_flags(flags); + return; + } + fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp; + fp &= (MAX_MODEM_BUF -1); + count = MIN(len, MAX_MODEM_BUF - fp); + if (count < len) { + memcpy(cs->hw.elsa.transbuf + fp, msg, count); + cs->hw.elsa.transcnt += count; + msg += count; + count = len - count; + fp = 0; + } + memcpy(cs->hw.elsa.transbuf + fp, msg, count); + cs->hw.elsa.transcnt += count; + if (cs->hw.elsa.transcnt && + !(cs->hw.elsa.IER & UART_IER_THRI)) { + cs->hw.elsa.IER |= UART_IER_THRI; + serial_outp(cs, UART_IER, cs->hw.elsa.IER); + } + restore_flags(flags); +} + +void +modem_set_init(struct IsdnCardState *cs) { + long flags; + int timeout; + +#define RCV_DELAY 20000 + save_flags(flags); + sti(); + modem_write_cmd(cs, MInit_1, strlen(MInit_1)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY); + modem_write_cmd(cs, MInit_2, strlen(MInit_2)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY); + modem_write_cmd(cs, MInit_3, strlen(MInit_3)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY); + modem_write_cmd(cs, MInit_4, strlen(MInit_4)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY ); + modem_write_cmd(cs, MInit_5, strlen(MInit_5)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY); + modem_write_cmd(cs, MInit_6, strlen(MInit_6)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY); + modem_write_cmd(cs, MInit_7, strlen(MInit_7)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY); + restore_flags(flags); +} + +void +modem_set_dial(struct IsdnCardState *cs, int outgoing) { + long flags; + int timeout; +#define RCV_DELAY 20000 + + save_flags(flags); + sti(); + modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY); + if (outgoing) + modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout)); + else + modem_write_cmd(cs, MInit_dialin, strlen(MInit_dialin)); + timeout = 1000; + while(timeout-- && cs->hw.elsa.transcnt) + udelay(1000); + debugl1(cs, "msi tout=%d", timeout); + udelay(RCV_DELAY); + restore_flags(flags); +} + +void +modem_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + if (pr == (PH_DATA | REQUEST)) { + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hscx.count = 0; + restore_flags(flags); + write_modem(st->l1.bcs); + } + } else if (pr == (PH_ACTIVATE | REQUEST)) { + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + set_arcofi(st->l1.bcs->cs, st->l1.bc); + mstartup(st->l1.bcs->cs); + modem_set_dial(st->l1.bcs->cs, test_bit(FLG_ORIG, &st->l2.flag)); + st->l1.bcs->cs->hw.elsa.MFlag=2; + } else if (pr == (PH_DEACTIVATE | REQUEST)) { + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + send_arcofi(st->l1.bcs->cs, ARCOFI_XOP_0, st->l1.bc, 0); + st->l1.bcs->cs->hw.elsa.MFlag=1; + } else { + printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr); + } +} + +int +setstack_elsa(struct PStack *st, struct BCState *bcs) +{ + + bcs->channel = st->l1.bc; + switch (st->l1.mode) { + case L1_MODE_HDLC: + case L1_MODE_TRANS: + if (open_hscxstate(st->l1.hardware, bcs)) + return (-1); + st->l2.l2l1 = hscx_l2l1; + break; + case L1_MODE_MODEM: + bcs->mode = L1_MODE_MODEM; + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf; + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->hw.hscx.rcvidx = 0; + bcs->tx_cnt = 0; + bcs->cs->hw.elsa.bcs = bcs; + st->l2.l2l1 = modem_l2l1; + break; + } + st->l1.bcs = bcs; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +void +init_modem(struct IsdnCardState *cs) { + + cs->bcs[0].BC_SetStack = setstack_elsa; + cs->bcs[1].BC_SetStack = setstack_elsa; + cs->bcs[0].BC_Close = close_elsastate; + cs->bcs[1].BC_Close = close_elsastate; + if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF, + GFP_ATOMIC))) { + printk(KERN_WARNING + "Elsa: No modem mem hw.elsa.rcvbuf\n"); + return; + } + if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF, + GFP_ATOMIC))) { + printk(KERN_WARNING + "Elsa: No modem mem hw.elsa.transbuf\n"); + kfree(cs->hw.elsa.rcvbuf); + cs->hw.elsa.rcvbuf = NULL; + return; + } + if (mstartup(cs)) { + printk(KERN_WARNING "Elsa: problem startup modem\n"); + } + modem_set_init(cs); +} + +void +release_modem(struct IsdnCardState *cs) { + + cs->hw.elsa.MFlag = 0; + if (cs->hw.elsa.transbuf) { + if (cs->hw.elsa.rcvbuf) { + mshutdown(cs); + kfree(cs->hw.elsa.rcvbuf); + cs->hw.elsa.rcvbuf = NULL; + } + kfree(cs->hw.elsa.transbuf); + cs->hw.elsa.transbuf = NULL; + } +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c --- v2.0.35/linux/drivers/isdn/hisax/fsm.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/fsm.c Sun Nov 15 10:32:58 1998 @@ -1,4 +1,4 @@ -/* $Id: fsm.c,v 1.4 1997/04/06 22:56:42 keil Exp $ +/* $Id: fsm.c,v 1.4.2.5 1998/11/03 00:06:23 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden @@ -7,6 +7,28 @@ * Fritz Elfert * * $Log: fsm.c,v $ + * Revision 1.4.2.5 1998/11/03 00:06:23 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.4.2.4 1998/05/27 18:05:21 keil + * HiSax 3.0 + * + * Revision 1.4.2.3 1998/03/07 23:15:20 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.4.2.2 1997/11/15 18:54:29 keil + * cosmetics + * + * Revision 1.4.2.1 1997/10/17 22:13:49 keil + * update to last hisax version + * + * Revision 1.6 1997/07/27 21:42:25 keil + * proof Fsm routines + * + * Revision 1.5 1997/06/26 11:10:05 keil + * Restart timer function added + * * Revision 1.4 1997/04/06 22:56:42 keil * Some cosmetic changes * @@ -26,19 +48,24 @@ #define FSM_TIMER_DEBUG 0 -void +HISAX_INITFUNC(void FsmNew(struct Fsm *fsm, - struct FsmNode *fnlist, int fncount) + struct FsmNode *fnlist, int fncount)) { int i; - fsm->jumpmatrix = (int *) - kmalloc(4L * fsm->state_count * fsm->event_count, GFP_KERNEL); - memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count); - - for (i = 0; i < fncount; i++) - fsm->jumpmatrix[fsm->state_count * fnlist[i].event + - fnlist[i].state] = (int) fnlist[i].routine; + fsm->jumpmatrix = (FSMFNPTR *) + kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL); + memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count); + + for (i = 0; i < fncount; i++) + if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) { + printk(KERN_ERR "FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n", + i,(long)fnlist[i].state,(long)fsm->state_count, + (long)fnlist[i].event,(long)fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; } void @@ -50,26 +77,26 @@ int FsmEvent(struct FsmInst *fi, int event, void *arg) { - void (*r) (struct FsmInst *, int, void *); - char str[80]; + FSMFNPTR r; - r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; + if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) { + printk(KERN_ERR "FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", + (long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count); + return(1); + } + r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; if (r) { - if (fi->debug) { - sprintf(str, "State %s Event %s", + if (fi->debug) + fi->printdebug(fi, "State %s Event %s", fi->fsm->strState[fi->state], fi->fsm->strEvent[event]); - fi->printdebug(fi, str); - } r(fi, event, arg); return (0); } else { - if (fi->debug) { - sprintf(str, "State %s Event %s no routine", + if (fi->debug) + fi->printdebug(fi, "State %s Event %s no routine", fi->fsm->strState[fi->state], fi->fsm->strEvent[event]); - fi->printdebug(fi, str); - } return (!0); } } @@ -77,25 +104,18 @@ void FsmChangeState(struct FsmInst *fi, int newstate) { - char str[80]; - fi->state = newstate; - if (fi->debug) { - sprintf(str, "ChangeState %s", + if (fi->debug) + fi->printdebug(fi, "ChangeState %s", fi->fsm->strState[newstate]); - fi->printdebug(fi, str); - } } static void FsmExpireTimer(struct FsmTimer *ft) { #if FSM_TIMER_DEBUG - if (ft->fi->debug) { - char str[40]; - sprintf(str, "FsmExpireTimer %lx", (long) ft); - ft->fi->printdebug(ft->fi, str); - } + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); #endif FsmEvent(ft->fi, ft->event, ft->arg); } @@ -107,11 +127,8 @@ ft->tl.function = (void *) FsmExpireTimer; ft->tl.data = (long) ft; #if FSM_TIMER_DEBUG - if (ft->fi->debug) { - char str[40]; - sprintf(str, "FsmInitTimer %lx", (long) ft); - ft->fi->printdebug(ft->fi, str); - } + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft); #endif init_timer(&ft->tl); } @@ -120,11 +137,8 @@ FsmDelTimer(struct FsmTimer *ft, int where) { #if FSM_TIMER_DEBUG - if (ft->fi->debug) { - char str[40]; - sprintf(str, "FsmDelTimer %lx %d", (long) ft, where); - ft->fi->printdebug(ft->fi, str); - } + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where); #endif del_timer(&ft->tl); } @@ -135,11 +149,9 @@ { #if FSM_TIMER_DEBUG - if (ft->fi->debug) { - char str[40]; - sprintf(str, "FsmAddTimer %lx %d %d", (long) ft, millisec, where); - ft->fi->printdebug(ft->fi, str); - } + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d", + (long) ft, millisec, where); #endif if (ft->tl.next || ft->tl.prev) { @@ -155,29 +167,22 @@ return 0; } -int -FsmTimerRunning(struct FsmTimer *ft) -{ - return (ft->tl.next != NULL); -} - void -jiftime(char *s, long mark) +FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) { - s += 8; - *s-- = '\0'; - *s-- = mark % 10 + '0'; - mark /= 10; - *s-- = mark % 10 + '0'; - mark /= 10; - *s-- = '.'; - *s-- = mark % 10 + '0'; - mark /= 10; - *s-- = mark % 6 + '0'; - mark /= 6; - *s-- = ':'; - *s-- = mark % 10 + '0'; - mark /= 10; - *s-- = mark % 10 + '0'; +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (ft->tl.next || ft->tl.prev) + del_timer(&ft->tl); + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hfc_2bds0.c linux/drivers/isdn/hisax/hfc_2bds0.c --- v2.0.35/linux/drivers/isdn/hisax/hfc_2bds0.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hfc_2bds0.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,1229 @@ +/* $Id: hfc_2bds0.c,v 1.1.2.9 1998/11/03 00:06:24 keil Exp $ + * + * specific routines for CCD's HFC 2BDS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bds0.c,v $ + * Revision 1.1.2.9 1998/11/03 00:06:24 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.8 1998/09/30 22:23:55 keil + * Fix missing line in setstack* + * + * Revision 1.1.2.7 1998/09/27 13:06:01 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.1.2.6 1998/06/27 22:54:07 keil + * make 16.3c working with 3.0 + * + * Revision 1.1.2.5 1998/05/27 18:05:23 keil + * HiSax 3.0 + * + * Revision 1.1.2.4 1998/04/08 21:54:35 keil + * Fix "ll_trans ..." message + * + * Revision 1.1.2.3 1998/04/04 21:59:20 keil + * Fixed B-channel access + * + * Revision 1.1.2.2 1998/01/27 22:40:35 keil + * fixed IRQ latency, B-channel selection and more + * + * Revision 1.1.2.1 1998/01/11 22:54:00 keil + * Teles 16.3c (HFC 2BDS0) first version + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" +#include +/* +#define KDEBUG_DEF +#include "kdebug.h" +*/ + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static void +dummyf(struct IsdnCardState *cs, u_char * data, int size) +{ + printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n"); +} + +static inline u_char +ReadReg(struct IsdnCardState *cs, int data, u_char reg) +{ + register u_char ret; + + if (data) { + if (cs->hw.hfcD.cip != reg) { + cs->hw.hfcD.cip = reg; + byteout(cs->hw.hfcD.addr | 1, reg); + } + ret = bytein(cs->hw.hfcD.addr); +#if HFC_REG_DEBUG + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) + debugl1(cs, "t3c RD %02x %02x", reg, ret); +#endif + } else + ret = bytein(cs->hw.hfcD.addr | 1); + return (ret); +} + +static inline void +WriteReg(struct IsdnCardState *cs, int data, u_char reg, u_char value) +{ + if (cs->hw.hfcD.cip != reg) { + cs->hw.hfcD.cip = reg; + byteout(cs->hw.hfcD.addr | 1, reg); + } + if (data) + byteout(cs->hw.hfcD.addr, value); +#if HFC_REG_DEBUG + if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB)) + debugl1(cs, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value); +#endif +} + +/* Interface functions */ + +static u_char +readreghfcd(struct IsdnCardState *cs, u_char offset) +{ + return(ReadReg(cs, HFCD_DATA, offset)); +} + +static void +writereghfcd(struct IsdnCardState *cs, u_char offset, u_char value) +{ + WriteReg(cs, HFCD_DATA, offset, value); +} + +void +set_cs_func(struct IsdnCardState *cs) +{ + cs->readisac = &readreghfcd; + cs->writeisac = &writereghfcd; + cs->readisacfifo = &dummyf; + cs->writeisacfifo = &dummyf; + cs->BC_Read_Reg = &ReadReg; + cs->BC_Write_Reg = &WriteReg; +} + +static inline int +WaitForBusy(struct IsdnCardState *cs) +{ + int to = 130; + + while (!(ReadReg(cs, HFCD_DATA, HFCD_STAT) & HFCD_BUSY) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: WaitForBusy timeout\n"); + return (to); +} + +static inline int +WaitNoBusy(struct IsdnCardState *cs) +{ + long flags; + int to = 130; + + while ((ReadReg(cs, HFCD_STATUS, HFCD_STATUS) & HFCD_BUSY) && to) { + save_flags(flags); + sti(); + udelay(1); + to--; + restore_flags(flags); + } + if (!to) + printk(KERN_WARNING "HiSax: WaitNoBusy timeout\n"); + return (to); +} + +static int +SelFiFo(struct IsdnCardState *cs, u_char FiFo) +{ + u_char cip; + long flags; + + + if (cs->hw.hfcD.fifo == FiFo) + return(1); + save_flags(flags); + cli(); + switch(FiFo) { + case 0: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B1; + break; + case 1: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B1; + break; + case 2: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B2; + break; + case 3: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B2; + break; + case 4: cip = HFCD_FIFO | HFCD_Z1 | HFCD_SEND; + break; + case 5: cip = HFCD_FIFO | HFCD_Z1 | HFCD_REC; + break; + default: + restore_flags(flags); + debugl1(cs, "SelFiFo Error"); + return(0); + } + cs->hw.hfcD.fifo = FiFo; + WaitNoBusy(cs); + cs->BC_Write_Reg(cs, HFCD_DATA, cip, 0); + sti(); + WaitForBusy(cs); + restore_flags(flags); + return(2); +} +static int +GetFreeFifoBytes_B(struct BCState *bcs) +{ + int s; + + if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2) + return (bcs->cs->hw.hfcD.bfifosize); + s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2]; + if (s <= 0) + s += bcs->cs->hw.hfcD.bfifosize; + s = bcs->cs->hw.hfcD.bfifosize - s; + return (s); +} + +static int +GetFreeFifoBytes_D(struct IsdnCardState *cs) +{ + int s; + + if (cs->hw.hfcD.f1 == cs->hw.hfcD.f2) + return (cs->hw.hfcD.dfifosize); + s = cs->hw.hfcD.send[cs->hw.hfcD.f1] - cs->hw.hfcD.send[cs->hw.hfcD.f2]; + if (s <= 0) + s += cs->hw.hfcD.dfifosize; + s = cs->hw.hfcD.dfifosize - s; + return (s); +} + +static int +ReadZReg(struct IsdnCardState *cs, u_char reg) +{ + int val; + + WaitNoBusy(cs); + val = 256 * ReadReg(cs, HFCD_DATA, reg | HFCB_Z_HIGH); + WaitNoBusy(cs); + val += ReadReg(cs, HFCD_DATA, reg | HFCB_Z_LOW); + return (val); +} + +static void +hfc_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static struct sk_buff +*hfc_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int idx; + int chksum; + long flags; + u_char stat, cip; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_empty_fifo"); + idx = 0; + save_flags(flags); + if (count > HSCX_BUFMAX + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too large"); + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + while (idx++ < count) { + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + } + skb = NULL; + } else if (count < 4) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too small"); + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + cli(); + while ((idx++ < count) && WaitNoBusy(cs)) + ReadReg(cs, HFCD_DATA_NODEB, cip); + skb = NULL; + } else if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFC: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + ptr = skb_put(skb, count - 3); + idx = 0; + cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel); + cli(); + while (idx < (count - 3)) { + cli(); + if (!WaitNoBusy(cs)) + break; + *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + ptr++; + idx++; + } + if (idx != count - 3) { + sti(); + debugl1(cs, "RFIFO BUSY error"); + printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); + dev_kfree_skb(skb, FREE_READ); + skb = NULL; + } else { + cli(); + WaitNoBusy(cs); + chksum = (ReadReg(cs, HFCD_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x", + bcs->channel, chksum, stat); + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb, FREE_READ); + skb = NULL; + } + } + } + sti(); + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC | + HFCB_REC | HFCB_CHANNEL(bcs->channel)); + sti(); + WaitForBusy(cs); + restore_flags(flags); + return (skb); +} + +static void +hfc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, fcnt; + int count; + u_char cip; + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + save_flags(flags); + cli(); + SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + cip = HFCB_FIFO | HFCB_F1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f1 = ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + cip = HFCB_FIFO | HFCB_F2 | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip); + bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + sti(); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)", + bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2, + bcs->hw.hfc.send[bcs->hw.hfc.f1]); + fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2; + if (fcnt < 0) + fcnt += 32; + if (fcnt > 30) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo more as 30 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes_B(bcs); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo %d count(%ld/%d),%lx", + bcs->channel, bcs->tx_skb->len, + count, current->state); + if (count < bcs->tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFCB_FIFO | HFCB_FIFO_IN | HFCB_SEND | HFCB_CHANNEL(bcs->channel); + idx = 0; + cli(); + WaitForBusy(cs); + WaitNoBusy(cs); + WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx++]); + while (idx < bcs->tx_skb->len) { + cli(); + if (!WaitNoBusy(cs)) + break; + WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx]); + sti(); + idx++; + } + if (idx != bcs->tx_skb->len) { + sti(); + debugl1(cs, "FIFO Send BUSY error"); + printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel); + } else { + bcs->tx_cnt -= bcs->tx_skb->len; + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + } + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F1_INC | HFCB_SEND | HFCB_CHANNEL(bcs->channel)); + sti(); + WaitForBusy(cs); + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + restore_flags(flags); + return; +} + +static void +hfc_send_data(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs,"send_data %d blocked", bcs->channel); +} + +void +main_rec_2bds0(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int z1, z2, rcnt; + u_char f1, f2, cip; + int receive, count = 5; + struct sk_buff *skb; + + save_flags(flags); + Begin: + count--; + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs,"rec_data %d blocked", bcs->channel); + restore_flags(flags); + return; + } + SelFiFo(cs, HFCB_REC | HFCB_CHANNEL(bcs->channel)); + cip = HFCB_FIFO | HFCB_F1 | HFCB_REC | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f1 = ReadReg(cs, HFCD_DATA, cip); + cip = HFCB_FIFO | HFCB_F2 | HFCB_REC | HFCB_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (f1 != f2) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc rec %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + cli(); + z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel)); + z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel)); + sti(); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfcD.bfifosize; + rcnt++; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + if ((skb = hfc_empty_fifo(bcs, rcnt))) { + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfc_sched_event(bcs, B_RCVBUFREADY); + } + rcnt = f1 -f2; + if (rcnt<0) + rcnt += 32; + if (rcnt>1) + receive = 1; + else + receive = 0; + } else + receive = 0; + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + if (count && receive) + goto Begin; + restore_flags(flags); + return; +} + +void +mode_2bs0(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HFCD bchannel mode %d bchan %d/%d", + mode, bc, bcs->channel); + bcs->mode = mode; + bcs->channel = bc; + switch (mode) { + case (L1_MODE_NULL): + if (bc) { + cs->hw.hfcD.conn |= 0x18; + cs->hw.hfcD.sctrl &= ~SCTRL_B2_ENA; + } else { + cs->hw.hfcD.conn |= 0x3; + cs->hw.hfcD.sctrl &= ~SCTRL_B1_ENA; + } + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfcD.ctmt |= 2; + cs->hw.hfcD.conn &= ~0x18; + cs->hw.hfcD.sctrl |= SCTRL_B2_ENA; + } else { + cs->hw.hfcD.ctmt |= 1; + cs->hw.hfcD.conn &= ~0x3; + cs->hw.hfcD.sctrl |= SCTRL_B1_ENA; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfcD.ctmt &= ~2; + cs->hw.hfcD.conn &= ~0x18; + cs->hw.hfcD.sctrl |= SCTRL_B2_ENA; + } else { + cs->hw.hfcD.ctmt &= ~1; + cs->hw.hfcD.conn &= ~0x3; + cs->hw.hfcD.sctrl |= SCTRL_B1_ENA; + } + break; + } + WriteReg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl); + WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + WriteReg(cs, HFCD_DATA, HFCD_CONN, cs->hw.hfcD.conn); +} + +static void +hfc_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; +/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); +*/ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); +/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); +*/ st->l1.bcs->tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + mode_2bs0(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +void +close_2bs0(struct BCState *bcs) +{ + mode_2bs0(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +int +setstack_2b(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_hfcstate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfc_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +static void +hfcd_bh(struct IsdnCardState *cs) +{ +/* struct PStack *stptr; +*/ + if (!cs) + return; +#if 0 + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } +#endif + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + switch (cs->ph_state) { + case (0): + l1_msg(cs, HW_RESET | INDICATION, NULL); + break; + case (3): + l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); + break; + case (8): + l1_msg(cs, HW_RSYNC | INDICATION, NULL); + break; + case (6): + l1_msg(cs, HW_INFO2 | INDICATION, NULL); + break; + case (7): + l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); + break; + default: + break; + } + } + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); +} + +void +sched_event_D(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static +int receive_dmsg(struct IsdnCardState *cs) +{ + struct sk_buff *skb; + long flags; + int idx; + int rcnt, z1, z2; + u_char stat, cip, f1, f2; + int chksum; + int count=5; + u_char *ptr; + + save_flags(flags); + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_dmsg blocked"); + restore_flags(flags); + return(1); + } + SelFiFo(cs, 4 | HFCD_REC); + cip = HFCD_FIFO | HFCD_F1 | HFCD_REC; + WaitNoBusy(cs); + f1 = cs->readisac(cs, cip) & 0xf; + cip = HFCD_FIFO | HFCD_F2 | HFCD_REC; + WaitNoBusy(cs); + f2 = cs->readisac(cs, cip) & 0xf; + while ((f1 != f2) && count--) { + z1 = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_REC); + z2 = ReadZReg(cs, HFCD_FIFO | HFCD_Z2 | HFCD_REC); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfcD.dfifosize; + rcnt++; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", + f1, f2, z1, z2, rcnt); + sti(); + idx = 0; + cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC; + if (rcnt > MAX_DFRAME_LEN + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo d: incoming packet too large"); + while (idx < rcnt) { + cli(); + if (!(WaitNoBusy(cs))) + break; + ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + idx++; + } + } else if (rcnt < 4) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo d: incoming packet too small"); + cli(); + while ((idx++ < rcnt) && WaitNoBusy(cs)) + ReadReg(cs, HFCD_DATA_NODEB, cip); + } else if ((skb = dev_alloc_skb(rcnt - 3))) { + SET_SKB_FREE(skb); + ptr = skb_put(skb, rcnt - 3); + while (idx < (rcnt - 3)) { + cli(); + if (!(WaitNoBusy(cs))) + break; + *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip); + sti(); + idx++; + ptr++; + } + if (idx != (rcnt - 3)) { + sti(); + debugl1(cs, "RFIFO D BUSY error"); + printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n"); + dev_kfree_skb(skb, FREE_READ); + skb = NULL; + } else { + cli(); + WaitNoBusy(cs); + chksum = (ReadReg(cs, HFCD_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += ReadReg(cs, HFCD_DATA, cip); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "empty_dfifo chksum %x stat %x", + chksum, stat); + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb, FREE_READ); + skb = NULL; + } else { + skb_queue_tail(&cs->rq, skb); + sched_event_D(cs, D_RCVBUFREADY); + } + } + } else + printk(KERN_WARNING "HFC: D receive out of memory\n"); + sti(); + WaitForBusy(cs); + cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC; + cli(); + WaitNoBusy(cs); + stat = ReadReg(cs, HFCD_DATA, cip); + sti(); + WaitForBusy(cs); + cip = HFCD_FIFO | HFCD_F2 | HFCD_REC; + cli(); + WaitNoBusy(cs); + f2 = cs->readisac(cs, cip) & 0xf; + } + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + restore_flags(flags); + return(1); +} + +static void +hfc_fill_dfifo(struct IsdnCardState *cs) +{ + long flags; + int idx, fcnt; + int count; + u_char cip; + + if (!cs->tx_skb) + return; + if (cs->tx_skb->len <= 0) + return; + + save_flags(flags); + cli(); + SelFiFo(cs, 4 | HFCD_SEND); + cip = HFCD_FIFO | HFCD_F1 | HFCD_SEND; + WaitNoBusy(cs); + cs->hw.hfcD.f1 = ReadReg(cs, HFCD_DATA, cip) & 0xf; + WaitNoBusy(cs); + cip = HFCD_FIFO | HFCD_F2 | HFCD_SEND; + cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf; + cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND); + sti(); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)", + cs->hw.hfcD.f1, cs->hw.hfcD.f2, + cs->hw.hfcD.send[cs->hw.hfcD.f1]); + fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2; + if (fcnt < 0) + fcnt += 16; + if (fcnt > 14) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_Dfifo more as 14 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes_D(cs); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfc_fill_Dfifo count(%ld/%d)", + cs->tx_skb->len, count); + if (count < cs->tx_skb->len) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfc_fill_Dfifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFCD_FIFO | HFCD_FIFO_IN | HFCD_SEND; + idx = 0; + cli(); + WaitForBusy(cs); + WaitNoBusy(cs); + WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx++]); + while (idx < cs->tx_skb->len) { + cli(); + if (!(WaitNoBusy(cs))) + break; + WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx]); + sti(); + idx++; + } + if (idx != cs->tx_skb->len) { + sti(); + debugl1(cs, "DFIFO Send BUSY error"); + printk(KERN_WARNING "HFC S DFIFO channel BUSY Error\n"); + } + WaitForBusy(cs); + cli(); + WaitNoBusy(cs); + ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND); + dev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_skb = NULL; + sti(); + WaitForBusy(cs); + restore_flags(flags); + return; +} + +static +struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel) +{ + if (cs->bcs[0].mode && (cs->bcs[0].channel == channel)) + return(&cs->bcs[0]); + else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel)) + return(&cs->bcs[1]); + else + return(NULL); +} + +void +hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val) +{ + u_char exval; + struct BCState *bcs; + int count=15; + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCD irq %x %s", val, + test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ? + "locked" : "unlocked"); + val &= cs->hw.hfcD.int_m1; + if (val & 0x40) { /* TE state machine irq */ + exval = cs->readisac(cs, HFCD_STATES) & 0xf; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_state chg %d->%d", cs->ph_state, + exval); + cs->ph_state = exval; + sched_event_D(cs, D_L1STATECHANGE); + val &= ~0x40; + } + while (val) { + save_flags(flags); + cli(); + if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + cs->hw.hfcD.int_s1 |= val; + restore_flags(flags); + return; + } + if (cs->hw.hfcD.int_s1 & 0x18) { + exval = val; + val = cs->hw.hfcD.int_s1; + cs->hw.hfcD.int_s1 = exval; + } + if (val & 0x08) { + if (!(bcs=Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x08 IRQ"); + } else + main_rec_2bds0(bcs); + } + if (val & 0x10) { + if (!(bcs=Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x10 IRQ"); + } else + main_rec_2bds0(bcs); + } + if (val & 0x01) { + if (!(bcs=Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x01 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs,"fill_data %d blocked", bcs->channel); + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs,"fill_data %d blocked", bcs->channel); + } else { + hfc_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x02) { + if (!(bcs=Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcd spurious 0x02 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs,"fill_data %d blocked", bcs->channel); + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs,"fill_data %d blocked", bcs->channel); + } else { + hfc_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x20) { /* receive dframe */ + receive_dmsg(cs); + } + if (val & 0x04) { /* dframe transmitted */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + sched_event_D(cs, D_CLEARBUSY); + if (cs->tx_skb) + if (cs->tx_skb->len) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfc_fill_dfifo irq blocked"); + } + goto afterXPR; + } else { + dev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfc_fill_dfifo irq blocked"); + } + } else + sched_event_D(cs, D_XMTBUFREADY); + } + afterXPR: + if (cs->hw.hfcD.int_s1 && count--) { + val = cs->hw.hfcD.int_s1; + cs->hw.hfcD.int_s1 = 0; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCD irq %x loop %d", val, 15-count); + } else + val = 0; + restore_flags(flags); + } +} + +static void +HFCD_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + + switch (pr) { + case (PH_DATA | REQUEST): + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + if (cs->tx_skb) { + skb_queue_tail(&cs->sq, skb); +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA Queued", 0); +#endif + } else { + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 0); +#endif + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfc_fill_dfifo blocked"); + + } + break; + case (PH_PULL | INDICATION): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA_PULLED", 0); +#endif + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfc_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfc_fill_dfifo blocked"); + break; + case (PH_PULL | REQUEST): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (HW_RESET | REQUEST): + cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */ + cs->hw.hfcD.mst_m |= HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION); + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (HW_ENABLE | REQUEST): + cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION); + break; + case (HW_DEACTIVATE | REQUEST): + cs->hw.hfcD.mst_m &= ~HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + break; + case (HW_INFO3 | REQUEST): + cs->hw.hfcD.mst_m |= HFCD_MASTER; + cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + break; +#if 0 + case (HW_TESTLOOP | REQUEST): + u_char val = 0; + if (1 & (int) arg) + val |= 0x0c; + if (2 & (int) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; +#endif + default: + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcd_l1hw unknown pr %4x", pr); + break; + } +} + +void +setstack_hfcd(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = HFCD_l1hw; +} + +static void +hfc_dbusy_timer(struct IsdnCardState *cs) +{ +#if 0 + struct PStack *stptr; + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy"); + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + stptr = stptr->next; + } + } +#endif +} + +__initfunc(unsigned int +*init_send_hfcd(int cnt)) +{ + int i, *send; + + if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfcd.send\n"); + return(NULL); + } + for (i = 0; i < cnt; i++) + send[i] = 0x1fff; + return(send); +} + +__initfunc(void +init2bds0(struct IsdnCardState *cs)) +{ + cs->setstack_d = setstack_hfcd; + cs->dbusytimer.function = (void *) hfc_dbusy_timer; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->tqueue.routine = (void *) (void *) hfcd_bh; + if (!cs->hw.hfcD.send) + cs->hw.hfcD.send = init_send_hfcd(16); + if (!cs->bcs[0].hw.hfc.send) + cs->bcs[0].hw.hfc.send = init_send_hfcd(32); + if (!cs->bcs[1].hw.hfc.send) + cs->bcs[1].hw.hfc.send = init_send_hfcd(32); + cs->BC_Send_Data = &hfc_send_data; + cs->bcs[0].BC_SetStack = setstack_2b; + cs->bcs[1].BC_SetStack = setstack_2b; + cs->bcs[0].BC_Close = close_2bs0; + cs->bcs[1].BC_Close = close_2bs0; + mode_2bs0(cs->bcs, 0, 0); + mode_2bs0(cs->bcs + 1, 0, 1); +} + +void +release2bds0(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.hfc.send) { + kfree(cs->bcs[0].hw.hfc.send); + cs->bcs[0].hw.hfc.send = NULL; + } + if (cs->bcs[1].hw.hfc.send) { + kfree(cs->bcs[1].hw.hfc.send); + cs->bcs[1].hw.hfc.send = NULL; + } + if (cs->hw.hfcD.send) { + kfree(cs->hw.hfcD.send); + cs->hw.hfcD.send = NULL; + } +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hfc_2bds0.h linux/drivers/isdn/hisax/hfc_2bds0.h --- v2.0.35/linux/drivers/isdn/hisax/hfc_2bds0.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hfc_2bds0.h Sun Nov 15 10:32:58 1998 @@ -0,0 +1,133 @@ +/* $Id: hfc_2bds0.h,v 1.1.2.2 1998/01/27 22:41:36 keil Exp $ + + * specific defines for CCD's HFC 2BDS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bds0.h,v $ + * Revision 1.1.2.2 1998/01/27 22:41:36 keil + * add set_cs_func() + * + * Revision 1.1.2.1 1998/01/11 22:54:02 keil + * Teles 16.3c (HFC 2BDS0) first version + * + * + */ + +#define HFCD_CIRM 0x18 +#define HFCD_CTMT 0x19 +#define HFCD_INT_M1 0x1A +#define HFCD_INT_M2 0x1B +#define HFCD_INT_S1 0x1E +#define HFCD_STAT 0x1C +#define HFCD_STAT_DISB 0x1D +#define HFCD_STATES 0x30 +#define HFCD_SCTRL 0x31 +#define HFCD_TEST 0x32 +#define HFCD_SQ 0x34 +#define HFCD_CLKDEL 0x37 +#define HFCD_MST_MODE 0x2E +#define HFCD_CONN 0x2F + +#define HFCD_FIFO 0x80 +#define HFCD_Z1 0x10 +#define HFCD_Z2 0x18 +#define HFCD_Z_LOW 0x00 +#define HFCD_Z_HIGH 0x04 +#define HFCD_F1_INC 0x12 +#define HFCD_FIFO_IN 0x16 +#define HFCD_F1 0x1a +#define HFCD_F2 0x1e +#define HFCD_F2_INC 0x22 +#define HFCD_FIFO_OUT 0x26 +#define HFCD_REC 0x01 +#define HFCD_SEND 0x00 + +#define HFCB_FIFO 0x80 +#define HFCB_Z1 0x00 +#define HFCB_Z2 0x08 +#define HFCB_Z_LOW 0x00 +#define HFCB_Z_HIGH 0x04 +#define HFCB_F1_INC 0x28 +#define HFCB_FIFO_IN 0x2c +#define HFCB_F1 0x30 +#define HFCB_F2 0x34 +#define HFCB_F2_INC 0x38 +#define HFCB_FIFO_OUT 0x3c +#define HFCB_REC 0x01 +#define HFCB_SEND 0x00 +#define HFCB_B1 0x00 +#define HFCB_B2 0x02 +#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1) + +#define HFCD_STATUS 0 +#define HFCD_DATA 1 +#define HFCD_DATA_NODEB 2 + +/* Status (READ) */ +#define HFCD_BUSY 0x01 +#define HFCD_BUSY_NBUSY 0x04 +#define HFCD_TIMER_ELAP 0x10 +#define HFCD_STATINT 0x20 +#define HFCD_FRAMEINT 0x40 +#define HFCD_ANYINT 0x80 + +/* CTMT (Write) */ +#define HFCD_CLTIMER 0x80 +#define HFCD_TIM25 0x00 +#define HFCD_TIM50 0x08 +#define HFCD_TIM400 0x10 +#define HFCD_TIM800 0x18 +#define HFCD_AUTO_TIMER 0x20 +#define HFCD_TRANSB2 0x02 +#define HFCD_TRANSB1 0x01 + +/* CIRM (Write) */ +#define HFCD_RESET 0x08 +#define HFCD_MEM8K 0x10 +#define HFCD_INTA 0x01 +#define HFCD_INTB 0x02 +#define HFCD_INTC 0x03 +#define HFCD_INTD 0x04 +#define HFCD_INTE 0x05 +#define HFCD_INTF 0x06 + +/* INT_M1;INT_S1 */ +#define HFCD_INTS_B1TRANS 0x01 +#define HFCD_INTS_B2TRANS 0x02 +#define HFCD_INTS_DTRANS 0x04 +#define HFCD_INTS_B1REC 0x08 +#define HFCD_INTS_B2REC 0x10 +#define HFCD_INTS_DREC 0x20 +#define HFCD_INTS_L1STATE 0x40 +#define HFCD_INTS_TIMER 0x80 + +/* INT_M2 */ +#define HFCD_IRQ_ENABLE 0x08 + +/* STATES */ +#define HFCD_LOAD_STATE 0x10 +#define HFCD_ACTIVATE 0x20 +#define HFCD_DO_ACTION 0x40 + +/* HFCD_MST_MODE */ +#define HFCD_MASTER 0x01 + +/* HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* HFCD_TEST */ +#define HFCD_AUTO_AWAKE 0x01 + +extern void main_irq_2bds0(struct BCState *bcs); +extern void init2bds0(struct IsdnCardState *cs); +extern void release2bds0(struct IsdnCardState *cs); +extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val); +extern void set_cs_func(struct IsdnCardState *cs); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hfc_2bs0.c linux/drivers/isdn/hisax/hfc_2bs0.c --- v2.0.35/linux/drivers/isdn/hisax/hfc_2bs0.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hfc_2bs0.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,601 @@ +/* $Id: hfc_2bs0.c,v 1.1.2.8 1998/11/03 00:06:29 keil Exp $ + + * specific routines for CCD's HFC 2BS0 + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: hfc_2bs0.c,v $ + * Revision 1.1.2.8 1998/11/03 00:06:29 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.7 1998/09/30 22:23:59 keil + * Fix missing line in setstack* + * + * Revision 1.1.2.6 1998/09/27 13:06:05 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.1.2.5 1998/05/27 18:05:27 keil + * HiSax 3.0 + * + * Revision 1.1.2.4 1998/04/08 21:54:38 keil + * Fix "ll_trans ..." message + * + * Revision 1.1.2.3 1998/04/04 21:59:23 keil + * Fixed B-channel access + * + * Revision 1.1.2.2 1997/11/15 18:54:27 keil + * cosmetics + * + * Revision 1.1.2.1 1997/10/17 22:10:41 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/11 17:31:33 keil + * Common part for HFC 2BS0 based cards + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bs0.h" +#include "isac.h" +#include "isdnl1.h" +#include + +static inline int +WaitForBusy(struct IsdnCardState *cs) +{ + int to = 130; + long flags; + u_char val; + + save_flags(flags); + cli(); + while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) { + val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 | + (cs->hw.hfc.cip & 3)); + udelay(1); + to--; + } + restore_flags(flags); + if (!to) { + printk(KERN_WARNING "HiSax: waitforBusy timeout\n"); + return (0); + } else + return (to); +} + +static inline int +WaitNoBusy(struct IsdnCardState *cs) +{ + int to = 125; + + while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) { + udelay(1); + to--; + } + if (!to) { + printk(KERN_WARNING "HiSax: waitforBusy timeout\n"); + return (0); + } else + return (to); +} + +int +GetFreeFifoBytes(struct BCState *bcs) +{ + int s; + + if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2) + return (bcs->cs->hw.hfc.fifosize); + s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2]; + if (s <= 0) + s += bcs->cs->hw.hfc.fifosize; + s = bcs->cs->hw.hfc.fifosize - s; + return (s); +} + +int +ReadZReg(struct BCState *bcs, u_char reg) +{ + int val; + + WaitNoBusy(bcs->cs); + val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH); + WaitNoBusy(bcs->cs); + val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW); + return (val); +} + +void +hfc_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +hfc_clear_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, cnt; + int rcnt, z1, z2; + u_char cip, f1, f2; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_clear_fifo"); + save_flags(flags); + cli(); + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + WaitForBusy(cs); + } + WaitNoBusy(cs); + f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + cnt = 32; + while (((f1 != f2) || (z1 != z2)) && cnt--) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc clear %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfc.fifosize; + if (rcnt) + rcnt++; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc clear %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + idx = 0; + while ((idx < rcnt) && WaitNoBusy(cs)) { + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + idx++; + } + if (f1 != f2) { + WaitNoBusy(cs); + cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + } + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + } + restore_flags(flags); + return; +} + + +static struct sk_buff +* +hfc_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int idx; + int chksum; + u_char stat, cip; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfc_empty_fifo"); + idx = 0; + if (count > HSCX_BUFMAX + 3) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too large"); + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + while ((idx++ < count) && WaitNoBusy(cs)) + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + return (NULL); + } + if (count < 4) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfc_empty_fifo: incoming packet too small"); + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + while ((idx++ < count) && WaitNoBusy(cs)) + cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + return (NULL); + } + if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFC: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + ptr = skb_put(skb, count - 3); + idx = 0; + cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel); + while ((idx < count - 3) && WaitNoBusy(cs)) { + *ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip); + idx++; + } + if (idx != count - 3) { + debugl1(cs, "RFIFO BUSY error"); + printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel); + dev_kfree_skb(skb, FREE_READ); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + return (NULL); + } + WaitNoBusy(cs); + chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8); + WaitNoBusy(cs); + chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip); + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x", + bcs->channel, chksum, stat); + if (stat) { + debugl1(cs, "FIFO CRC error"); + dev_kfree_skb(skb, FREE_READ); + skb = NULL; + } + WaitNoBusy(cs); + stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC | + HFC_CHANNEL(bcs->channel)); + WaitForBusy(cs); + } + return (skb); +} + +static void +hfc_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + long flags; + int idx, fcnt; + int count; + u_char cip; + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + save_flags(flags); + cli(); + cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel); + if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + WaitForBusy(cs); + } + WaitNoBusy(cs); + bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel)); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)", + bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2, + bcs->hw.hfc.send[bcs->hw.hfc.f1]); + fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2; + if (fcnt < 0) + fcnt += 32; + if (fcnt > 30) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo more as 30 frames"); + restore_flags(flags); + return; + } + count = GetFreeFifoBytes(bcs); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)", + bcs->channel, bcs->tx_skb->len, + count); + if (count < bcs->tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel); + idx = 0; + while ((idx < bcs->tx_skb->len) && WaitNoBusy(cs)) + cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->tx_skb->data[idx++]); + if (idx != bcs->tx_skb->len) { + debugl1(cs, "FIFO Send BUSY error"); + printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel); + } else { + count = bcs->tx_skb->len; + bcs->tx_cnt -= count; + if (PACKET_NOACK == bcs->tx_skb->pkt_type) + count = -1; + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + WaitForBusy(cs); + WaitNoBusy(cs); + cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel)); + if (bcs->st->lli.l1writewakeup && (count >= 0)) + bcs->st->lli.l1writewakeup(bcs->st, count); + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + restore_flags(flags); + return; +} + +void +main_irq_hfc(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int z1, z2, rcnt; + u_char f1, f2, cip; + int receive, transmit, count = 5; + struct sk_buff *skb; + + save_flags(flags); + Begin: + cli(); + count--; + cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel); + if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) { + cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip); + WaitForBusy(cs); + } + WaitNoBusy(cs); + f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel); + WaitNoBusy(cs); + f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip); + if (f1 != f2) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc rec %d f1(%d) f2(%d)", + bcs->channel, f1, f2); + WaitForBusy(cs); + z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel)); + z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel)); + rcnt = z1 - z2; + if (rcnt < 0) + rcnt += cs->hw.hfc.fifosize; + rcnt++; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, z1, z2, rcnt); +/* sti(); */ + if ((skb = hfc_empty_fifo(bcs, rcnt))) { + skb_queue_tail(&bcs->rqueue, skb); + hfc_sched_event(bcs, B_RCVBUFREADY); + } + receive = 1; + } else + receive = 0; + restore_flags(flags); + udelay(1); + cli(); + if (bcs->tx_skb) { + transmit = 1; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hfc_fill_fifo(bcs); + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) + transmit = 0; + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + transmit = 1; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hfc_fill_fifo(bcs); + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) + transmit = 0; + } else { + transmit = 0; + hfc_sched_event(bcs, B_XMTBUFREADY); + } + } + restore_flags(flags); + if ((receive || transmit) && count) + goto Begin; + return; +} + +void +mode_hfc(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HFC 2BS0 mode %d bchan %d/%d", + mode, bc, bcs->channel); + bcs->mode = mode; + bcs->channel = bc; + + switch (mode) { + case (L1_MODE_NULL): + if (bc) + cs->hw.hfc.isac_spcr &= ~0x03; + else + cs->hw.hfc.isac_spcr &= ~0x0c; + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfc.ctmt |= 1; + cs->hw.hfc.isac_spcr &= ~0x03; + cs->hw.hfc.isac_spcr |= 0x02; + } else { + cs->hw.hfc.ctmt |= 2; + cs->hw.hfc.isac_spcr &= ~0x0c; + cs->hw.hfc.isac_spcr |= 0x08; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfc.ctmt &= ~1; + cs->hw.hfc.isac_spcr &= ~0x03; + cs->hw.hfc.isac_spcr |= 0x02; + } else { + cs->hw.hfc.ctmt &= ~2; + cs->hw.hfc.isac_spcr &= ~0x0c; + cs->hw.hfc.isac_spcr |= 0x08; + } + break; + } + cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt); + cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr); + if (mode) + hfc_clear_fifo(bcs); +} + +static void +hfc_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + mode_hfc(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + + +void +close_hfcstate(struct BCState *bcs) +{ + mode_hfc(bcs, 0, bcs->channel); + if (test_bit(BC_FLG_INIT, &bcs->Flag)) { + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); +} + +static int +open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +int +setstack_hfc(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_hfcstate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfc_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +__initfunc(void +init_send(struct BCState *bcs)) +{ + int i; + + if (!(bcs->hw.hfc.send = kmalloc(32 * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfc.send\n"); + return; + } + for (i = 0; i < 32; i++) + bcs->hw.hfc.send[i] = 0x1fff; +} + +__initfunc(void +inithfc(struct IsdnCardState *cs)) +{ + init_send(&cs->bcs[0]); + init_send(&cs->bcs[1]); + cs->BC_Send_Data = &hfc_fill_fifo; + cs->bcs[0].BC_SetStack = setstack_hfc; + cs->bcs[1].BC_SetStack = setstack_hfc; + cs->bcs[0].BC_Close = close_hfcstate; + cs->bcs[1].BC_Close = close_hfcstate; + mode_hfc(cs->bcs, 0, 0); + mode_hfc(cs->bcs + 1, 0, 0); +} + +void +releasehfc(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.hfc.send) { + kfree(cs->bcs[0].hw.hfc.send); + cs->bcs[0].hw.hfc.send = NULL; + } + if (cs->bcs[1].hw.hfc.send) { + kfree(cs->bcs[1].hw.hfc.send); + cs->bcs[1].hw.hfc.send = NULL; + } +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hfc_2bs0.h linux/drivers/isdn/hisax/hfc_2bs0.h --- v2.0.35/linux/drivers/isdn/hisax/hfc_2bs0.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hfc_2bs0.h Sun Nov 15 10:32:58 1998 @@ -0,0 +1,65 @@ +/* $Id: hfc_2bs0.h,v 1.1.2.1 1997/10/17 22:10:43 keil Exp $ + + * specific defines for CCD's HFC 2BS0 + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hfc_2bs0.h,v $ + * Revision 1.1.2.1 1997/10/17 22:10:43 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/11 17:31:34 keil + * Common part for HFC 2BS0 based cards + * + * + */ + +#define HFC_CTMT 0xe0 +#define HFC_CIRM 0xc0 +#define HFC_CIP 0x80 +#define HFC_Z1 0x00 +#define HFC_Z2 0x08 +#define HFC_Z_LOW 0x00 +#define HFC_Z_HIGH 0x04 +#define HFC_F1_INC 0x28 +#define HFC_FIFO_IN 0x2c +#define HFC_F1 0x30 +#define HFC_F2 0x34 +#define HFC_F2_INC 0x38 +#define HFC_FIFO_OUT 0x3c +#define HFC_B1 0x00 +#define HFC_B2 0x02 +#define HFC_REC 0x01 +#define HFC_SEND 0x00 +#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1) + +#define HFC_STATUS 0 +#define HFC_DATA 1 +#define HFC_DATA_NODEB 2 + +/* Status (READ) */ +#define HFC_BUSY 0x01 +#define HFC_TIMINT 0x02 +#define HFC_EXTINT 0x04 + +/* CTMT (Write) */ +#define HFC_CLTIMER 0x10 +#define HFC_TIM50MS 0x08 +#define HFC_TIMIRQE 0x04 +#define HFC_TRANSB2 0x02 +#define HFC_TRANSB1 0x01 + +/* CIRM (Write) */ +#define HFC_RESET 0x08 +#define HFC_MEM8K 0x10 +#define HFC_INTA 0x01 +#define HFC_INTB 0x02 +#define HFC_INTC 0x03 +#define HFC_INTD 0x04 +#define HFC_INTE 0x05 +#define HFC_INTF 0x06 + +extern void main_irq_hfc(struct BCState *bcs); +extern void inithfc(struct IsdnCardState *cs); +extern void releasehfc(struct IsdnCardState *cs); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h --- v2.0.35/linux/drivers/isdn/hisax/hisax.h Wed Oct 15 15:25:02 1997 +++ linux/drivers/isdn/hisax/hisax.h Sun Nov 15 10:32:58 1998 @@ -1,52 +1,96 @@ -/* $Id: hisax.h,v 1.13 1997/04/06 22:54:12 keil Exp $ +/* $Id: hisax.h,v 1.13.2.19 1998/11/05 21:11:17 keil Exp $ * Basic declarations, defines and prototypes * * $Log: hisax.h,v $ - * Revision 1.13 1997/04/06 22:54:12 keil - * Using SKB's + * Revision 1.13.2.19 1998/11/05 21:11:17 keil + * AVM PnP support * - * Revision 1.12 1997/03/23 21:45:45 keil - * Add support for ELSA PCMCIA + * Revision 1.13.2.18 1998/11/03 00:06:33 keil + * certification related changes + * fixed logging for smaller stack use * - * Revision 1.11 1997/02/11 01:36:02 keil - * New Param structure + * Revision 1.13.2.17 1998/10/11 19:33:48 niemann + * Added new IPAC based cards. + * Code cleanup and simplified (sedlbauer.c) * - * Revision 1.10 1997/02/09 00:23:52 keil - * new interface handling, one interface per card + * Revision 1.13.2.16 1998/10/04 23:04:54 keil + * ISAR works now * - * Revision 1.9 1997/01/27 23:18:44 keil - * prototype for releasestack_isdnl3 + * Revision 1.13.2.15 1998/09/30 22:28:04 keil + * more work for isar support * - * Revision 1.8 1997/01/27 16:02:37 keil - * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype + * Revision 1.13.2.14 1998/09/27 13:06:09 keil + * Apply most changes from 2.1.X (HiSax 3.1) * - * Revision 1.7 1997/01/21 22:22:14 keil - * changes for 2.0; Elsa Quickstep support + * Revision 1.13.2.13 1998/08/25 14:01:30 calle + * Ported driver for AVM Fritz!Card PCI from the 2.1 tree. + * I could not test it. * - * Revision 1.6 1997/01/04 13:48:28 keil - * primitiv for MDL_REMOVE added + * Revision 1.13.2.12 1998/07/15 14:43:33 calle + * Support for AVM passive PCMCIA cards: + * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 * - * Revision 1.5 1996/12/08 19:49:19 keil - * Monitor channel support + * Revision 1.13.2.11 1998/05/27 18:05:30 keil + * HiSax 3.0 * - * Revision 1.4 1996/11/18 15:35:39 keil - * some changes for ELSA cards + * Revision 1.13.2.10 1998/04/11 18:43:16 keil + * New cards * - * Revision 1.3 1996/11/05 19:37:23 keil - * using config.h + * Revision 1.13.2.9 1998/03/07 23:15:21 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.2 1996/10/27 22:21:52 keil - * CallFlags for broadcast messages + * Revision 1.13.2.8 1998/02/11 14:23:10 keil + * support for Dr Neuhaus Niccy PnP and PCI * - * Revision 1.1 1996/10/13 20:03:46 keil - * Initial revision + * Revision 1.13.2.7 1998/02/09 11:21:22 keil + * Sedlbauer PCMCIA support from Marcus Niemann * + * Revision 1.13.2.6 1998/02/03 23:16:12 keil + * german AOC * + * Revision 1.13.2.5 1998/01/27 22:42:42 keil + * changes for new teles 16.3c and dynalink ---> asuscom + * + * Revision 1.13.2.4 1998/01/11 22:55:17 keil + * 16.3c support + * + * Revision 1.13.2.3 1997/11/27 12:31:59 keil + * Working netjet driver + * + * Revision 1.13.2.2 1997/11/15 18:55:43 keil + * New init, new cards + * + * Revision 1.13.2.1 1997/10/17 22:13:51 keil + * update to last hisax version + * + * Revision 2.6 1997/09/11 17:25:51 keil + * Add new cards + * + * Revision 2.5 1997/08/03 14:36:31 keil + * Implement RESTART procedure + * + * Revision 2.4 1997/07/31 19:25:20 keil + * PTP_DATA_LINK support + * + * Revision 2.3 1997/07/31 11:50:17 keil + * ONE TEI and FIXED TEI handling + * + * Revision 2.2 1997/07/30 17:13:02 keil + * more changes for 'One TEI per card' + * + * Revision 2.1 1997/07/27 21:45:13 keil + * new main structures + * + * Revision 2.0 1997/06/26 11:06:27 keil + * New card and L1 interface. + * Eicon.Diehl Diva and Dynalink IS64PH support + * + * old changes removed KKe * */ -#include #include +#include #include #include #include @@ -64,142 +108,113 @@ #include #include #include +#include -#define PH_ACTIVATE 1 -#define PH_DATA 2 -#define PH_DEACTIVATE 3 - -#define MDL_ASSIGN 4 -#define DL_UNIT_DATA 5 -#define SC_STARTUP 6 -#define CC_ESTABLISH 7 -#define DL_ESTABLISH 8 -#define DL_DATA 9 -#define CC_S_STATUS_ENQ 10 - -#define CC_CONNECT 15 -#define CC_CONNECT_ACKNOWLEDGE 16 -#define CO_EOF 17 -#define SC_DISCONNECT 18 -#define CO_DTMF 19 -#define DL_RELEASE 20 -#define DL_FLUSH 21 - -#define CO_ALARM 22 -#define CC_REJECT 23 - -#define CC_SETUP_REQ 24 -#define CC_SETUP_CNF 25 -#define CC_SETUP_IND 26 -#define CC_SETUP_RSP 27 -#define CC_SETUP_COMPLETE_IND 28 - -#define CC_DISCONNECT_REQ 29 -#define CC_DISCONNECT_IND 30 - -#define CC_RELEASE_CNF 31 -#define CC_RELEASE_IND 32 -#define CC_RELEASE_REQ 33 - -#define CC_REJECT_REQ 34 - -#define CC_PROCEEDING_IND 35 - -#define CC_DLRL 36 -#define CC_DLEST 37 - -#define CC_ALERTING_REQ 38 -#define CC_ALERTING_IND 39 - -#define DL_STOP 40 -#define DL_START 41 - -#define MDL_NOTEIPROC 46 - -#define LC_ESTABLISH 47 -#define LC_RELEASE 48 - -#define PH_REQUEST_PULL 49 -#define PH_PULL_ACK 50 -#define PH_DATA_PULLED 51 -#define CC_INFO_CHARGE 52 - -#define CC_MORE_INFO 53 -#define CC_IGNORE 54 - -#define MDL_REMOVE 56 -#define MDL_VERIFY 57 - -#define CC_T303 60 -#define CC_T304 61 -#define CC_T305 62 -#define CC_T308_1 64 -#define CC_T308_2 65 -#define CC_T310 66 -#define CC_T313 67 -#define CC_T318 68 -#define CC_T319 69 - -#define CC_NOSETUP_RSP_ERR 70 -#define CC_SETUP_ERR 71 -#define CC_CONNECT_ERR 72 -#define CC_RELEASE_ERR 73 - -/* - * Message-Types - */ - -#define MT_ALERTING 0x01 -#define MT_CALL_PROCEEDING 0x02 -#define MT_CONNECT 0x07 -#define MT_CONNECT_ACKNOWLEDGE 0x0f -#define MT_PROGRESS 0x03 -#define MT_SETUP 0x05 -#define MT_SETUP_ACKNOWLEDGE 0x0d -#define MT_RESUME 0x26 -#define MT_RESUME_ACKNOWLEDGE 0x2e -#define MT_RESUME_REJECT 0x22 -#define MT_SUSPEND 0x25 -#define MT_SUSPEND_ACKNOWLEDGE 0x2d -#define MT_SUSPEND_REJECT 0x21 -#define MT_USER_INFORMATION 0x20 -#define MT_DISCONNECT 0x45 -#define MT_RELEASE 0x4d -#define MT_RELEASE_COMPLETE 0x5a -#define MT_RESTART 0x46 -#define MT_RESTART_ACKNOWLEDGE 0x4e -#define MT_SEGMENT 0x60 -#define MT_CONGESTION_CONTROL 0x79 -#define MT_INFORMATION 0x7b -#define MT_FACILITY 0x62 -#define MT_NOTIFY 0x6e -#define MT_STATUS 0x7d -#define MT_STATUS_ENQUIRY 0x75 - -#define IE_CAUSE 0x08 +#define REQUEST 0 +#define CONFIRM 1 +#define INDICATION 2 +#define RESPONSE 3 + +#define HW_ENABLE 0x0000 +#define HW_RESET 0x0004 +#define HW_POWERUP 0x0008 +#define HW_ACTIVATE 0x0010 +#define HW_DEACTIVATE 0x0018 +#define HW_INFO2 0x0020 +#define HW_INFO3 0x0030 +#define HW_INFO4_P8 0x0040 +#define HW_INFO4_P10 0x0048 +#define HW_RSYNC 0x0060 +#define HW_TESTLOOP 0x0070 +#define CARD_RESET 0x00F0 +#define CARD_SETIRQ 0x00F1 +#define CARD_INIT 0x00F2 +#define CARD_RELEASE 0x00F3 +#define CARD_TEST 0x00F4 +#define CARD_AUX_IND 0x00F5 +#define CARD_LOAD_FIRM 0x00F6 + +#define PH_ACTIVATE 0x0100 +#define PH_DEACTIVATE 0x0110 +#define PH_DATA 0x0120 +#define PH_PULL 0x0130 +#define PH_TESTLOOP 0x0140 +#define PH_PAUSE 0x0150 +#define MPH_ACTIVATE 0x0180 +#define MPH_DEACTIVATE 0x0190 +#define MPH_INFORMATION 0x01A0 + +#define DL_ESTABLISH 0x0200 +#define DL_RELEASE 0x0210 +#define DL_DATA 0x0220 +#define DL_FLUSH 0x0224 +#define DL_UNIT_DATA 0x0230 +#define MDL_ASSIGN 0x0280 +#define MDL_REMOVE 0x0284 +#define MDL_ERROR 0x0288 +#define MDL_INFO_SETUP 0x02E0 +#define MDL_INFO_CONN 0x02E4 +#define MDL_INFO_REL 0x02E8 + +#define CC_SETUP 0x0300 +#define CC_RESUME 0x0304 +#define CC_MORE_INFO 0x0310 +#define CC_IGNORE 0x0320 +#define CC_REJECT 0x0324 +#define CC_SETUP_COMPL 0x0330 +#define CC_PROCEEDING 0x0340 +#define CC_ALERTING 0x0344 +#define CC_CONNECT 0x0350 +#define CC_CHARGE 0x0354 +#define CC_DISCONNECT 0x0360 +#define CC_RELEASE 0x0368 +#define CC_SUSPEND 0x0370 +#define CC_T303 0x0383 +#define CC_T304 0x0384 +#define CC_T305 0x0385 +#define CC_T308_1 0x0388 +#define CC_T308_2 0x0389 +#define CC_T310 0x0390 +#define CC_T313 0x0393 +#define CC_T318 0x0398 +#define CC_T319 0x0399 +#define CC_NOSETUP_RSP 0x03E0 +#define CC_SETUP_ERR 0x03E1 +#define CC_SUSPEND_ERR 0x03E2 +#define CC_RESUME_ERR 0x03E3 +#define CC_CONNECT_ERR 0x03E4 +#define CC_RELEASE_ERR 0x03E5 +#define CC_DLRL 0x03F0 +#define CC_RESTART 0x03F4 -struct HscxIoctlArg { - int channel; - int mode; - int transbufsize; -}; #ifdef __KERNEL__ -#undef DEBUG_MAGIC - -#define MAX_DFRAME_LEN 3072 +#define MAX_DFRAME_LEN 260 +#define MAX_DFRAME_LEN_L1 300 #define HSCX_BUFMAX 4096 #define MAX_DATA_SIZE (HSCX_BUFMAX - 4) -#define MAX_DATA_MEM (HSCX_BUFMAX * 2) +#define MAX_DATA_MEM (HSCX_BUFMAX + 64) +#define RAW_BUFMAX (((HSCX_BUFMAX*6)/5) + 5) #define MAX_HEADER_LEN 4 #define MAX_WINDOW 8 +#define MAX_MON_FRAME 32 +#define MAX_DLOG_SPACE 2048 +#define MAX_BLOG_SPACE 256 + +/* #define I4L_IRQ_FLAG SA_INTERRUPT */ +#define I4L_IRQ_FLAG 0 /* * Statemachine */ + +struct FsmInst; + +typedef void (* FSMFNPTR)(struct FsmInst *, int, void *); + struct Fsm { - int *jumpmatrix; + FSMFNPTR *jumpmatrix; int state_count, event_count; char **strEvent, **strState; }; @@ -210,7 +225,7 @@ int debug; void *userdata; int userint; - void (*printdebug) (struct FsmInst *, char *); + void (*printdebug) (struct FsmInst *, char *, ...); }; struct FsmNode { @@ -226,73 +241,111 @@ }; struct L3Timer { - struct PStack *st; + struct l3_process *pc; struct timer_list tl; int event; }; +#define FLG_L1_ACTIVATING 1 +#define FLG_L1_ACTIVATED 2 +#define FLG_L1_DEACTTIMER 3 +#define FLG_L1_ACTTIMER 4 +#define FLG_L1_T3RUN 5 +#define FLG_L1_PULL_REQ 6 + struct Layer1 { void *hardware; - int hscx; + struct BCState *bcs; struct PStack **stlistp; - int act_state; + int Flags; + struct FsmInst l1m; + struct FsmTimer timer; void (*l1l2) (struct PStack *, int, void *); - void (*l1man) (struct PStack *, int, void *); - int hscxmode, hscxchannel, requestpull; + void (*l1hw) (struct PStack *, int, void *); + void (*l1tei) (struct PStack *, int, void *); + int mode, bc; + int delay; }; +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 +#define PACKET_NOACK 250 + +/* Layer2 Flags */ + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 +#define FLG_L1_ACTIV 12 +#define FLG_ESTAB_PEND 13 +#define FLG_PTP 14 +#define FLG_FIXED_TEI 15 + struct Layer2 { - int sap, tei, ces; - int extended, laptype; - int uihsize, ihsize; + int tei; + int sap; + int maxlen; + unsigned int flag; int vs, va, vr; - struct sk_buff_head i_queue; - int window, orig; - int rejexp; - int debug; - struct sk_buff *windowar[MAX_WINDOW]; + int rc; + int window; int sow; - struct FsmInst l2m; + struct sk_buff *windowar[MAX_WINDOW]; + struct sk_buff_head i_queue; + struct sk_buff_head ui_queue; void (*l2l1) (struct PStack *, int, void *); - void (*l2l1discardq) (struct PStack *, int, void *, int); - void (*l2man) (struct PStack *, int, void *); void (*l2l3) (struct PStack *, int, void *); void (*l2tei) (struct PStack *, int, void *); - struct FsmTimer t200_timer, t203_timer; - int t200, n200, t203; - int rc, t200_running; - char debug_id[32]; + struct FsmInst l2m; + struct FsmTimer t200, t203; + int T200, N200, T203; + int debug; + char debug_id[16]; }; struct Layer3 { void (*l3l4) (struct PStack *, int, void *); void (*l3l2) (struct PStack *, int, void *); - int state, callref; - struct L3Timer timer; - int t303, t304, t305, t308, t310, t313, t318, t319; - int n_t303; + struct FsmInst l3m; + struct sk_buff_head squeue; + struct l3_process *proc; + struct l3_process *global; + int N303; int debug; - int channr; + char debug_id[8]; }; -struct Layer4 { +struct LLInterface { void (*l4l3) (struct PStack *, int, void *); void *userdata; - void (*l1writewakeup) (struct PStack *); - void (*l2writewakeup) (struct PStack *); + void (*l1writewakeup) (struct PStack *, int); + void (*l2writewakeup) (struct PStack *, int); }; + struct Management { - void (*manl1) (struct PStack *, int, void *); - void (*manl2) (struct PStack *, int, void *); - void (*teil2) (struct PStack *, int, void *); + int ri; + struct FsmInst tei_m; + struct FsmTimer t202; + int T202, N202, debug; + void (*layer) (struct PStack *, int, void *); }; + struct Param { int cause; int loc; int bchannel; - int callref; /* Callreferenz Number */ setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ int chargeinfo; /* Charge Info - only for 1tr6 in * the moment @@ -300,95 +353,368 @@ int spv; /* SPV Flag */ }; + struct PStack { struct PStack *next; struct Layer1 l1; struct Layer2 l2; struct Layer3 l3; - struct Layer4 l4; + struct LLInterface lli; struct Management ma; - struct Param *pa; int protocol; /* EDSS1 or 1TR6 */ }; -struct HscxState { - int inuse, init, active; - struct IsdnCardState *sp; - int hscx, mode; - u_char *rcvbuf; /* B-Channel receive Buffer */ - int rcvidx; /* B-Channel receive Buffer Index */ - struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +struct l3_process { + int callref; + int state; + struct L3Timer timer; + int N303; + int debug; + struct Param para; + struct Channel *chan; + struct PStack *st; + struct l3_process *next; +}; + +struct hscx_hw { + int hscx; + int rcvidx; + int count; /* Current skb sent count */ + u_char *rcvbuf; /* B-Channel receive Buffer */ +}; + +struct isar_reg { + unsigned int Flags; + volatile u_char bstat; + volatile u_char iis; + volatile u_char cmsb; + volatile u_char clsb; + volatile u_char par[8]; +}; + +struct isar_hw { + int dpath; + int rcvidx; + int txcnt; + int mml; + u_char *rcvbuf; /* B-Channel receive Buffer */ + struct isar_reg *reg; +}; + +struct hdlc_stat_reg { + u_char cmd __attribute__((packed)); + u_char xml __attribute__((packed)); + u_char mode __attribute__((packed)); + u_char fill __attribute__((packed)); +}; + +struct hdlc_hw { + union { + u_int ctrl; + struct hdlc_stat_reg sr; + } ctrl; + u_int stat; + int rcvidx; + int count; /* Current skb sent count */ + u_char *rcvbuf; /* B-Channel receive Buffer */ +}; + +struct hfcB_hw { + unsigned int *send; + int f1; + int f2; +}; + +struct tiger_hw { + u_int *send; + u_int *s_irq; + u_int *s_end; + u_int *sendp; + u_int *rec; + int free; + u_char *rcvbuf; + u_char *sendbuf; + u_char *sp; + int sendcnt; + u_int s_tot; + u_int r_bitcnt; + u_int r_tot; + u_int r_err; + u_int r_fcs; + u_char r_state; + u_char r_one; + u_char r_val; + u_char s_state; +}; + +struct amd7930_hw { + u_char *tx_buff; + u_char *rv_buff; + int rv_buff_in; + int rv_buff_out; + struct sk_buff *rv_skb; + struct hdlc_state *hdlc_state; + struct tq_struct tq_rcv; + struct tq_struct tq_xmt; +}; + + +#define BC_FLG_INIT 1 +#define BC_FLG_ACTIV 2 +#define BC_FLG_BUSY 3 +#define BC_FLG_NOFRAME 4 +#define BC_FLG_HALF 5 +#define BC_FLG_EMPTY 6 + +#define L1_MODE_NULL 0 +#define L1_MODE_TRANS 1 +#define L1_MODE_HDLC 2 +#define L1_MODE_MODEM 7 + +struct BCState { + int channel; + int mode; + int Flag; + struct IsdnCardState *cs; int tx_cnt; /* B-Channel transmit counter */ - int count; /* Current skb sent count */ + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ struct sk_buff_head rqueue; /* B-Channel receive Queue */ - struct sk_buff_head squeue; /* B-Channel receive Queue */ + struct sk_buff_head squeue; /* B-Channel send Queue */ struct PStack *st; + u_char *blog; + struct timer_list transbusy; struct tq_struct tqueue; int event; -#ifdef DEBUG_MAGIC - int magic; /* 301270 */ -#endif -}; - -struct LcFsm { - struct FsmInst lcfi; - int type; - struct Channel *ch; - void (*lccall) (struct LcFsm *, int, void *); - struct PStack *st; - int l2_establish; - int l2_start; - struct FsmTimer act_timer; - char debug_id[32]; + int (*BC_SetStack) (struct PStack *, struct BCState *); + void (*BC_Close) (struct BCState *); + union { + struct hscx_hw hscx; + struct hdlc_hw hdlc; + struct isar_hw isar; + struct hfcB_hw hfc; + struct tiger_hw tiger; + struct amd7930_hw amd7930; + } hw; }; struct Channel { - struct PStack ds, is; - struct IsdnCardState *sp; - int hscx; + struct PStack *b_st, *d_st; + struct IsdnCardState *cs; + struct BCState *bcs; int chan; int incoming; struct FsmInst fi; - struct LcFsm lc_d, lc_b; - struct Param para; struct FsmTimer drel_timer, dial_timer; int debug; -#ifdef DEBUG_MAGIC - int magic; /* 301272 */ -#endif int l2_protocol, l2_active_protocol; - int l2_primitive, l2_headersize; + int l3_protocol; int data_open; - int outcallref; - int impair; + struct l3_process *proc; + setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ int Flags; /* for remembering action done in l4 */ int leased; }; -struct IsdnCardState { -#ifdef DEBUG_MAGIC - int magic; -#endif - unsigned char typ; - unsigned char subtyp; - int protocol; - unsigned int irq; +struct elsa_hw { + unsigned int base; + unsigned int cfg; + unsigned int ctrl; + unsigned int ale; + unsigned int isac; + unsigned int itac; + unsigned int hscx; + unsigned int trig; + unsigned int timer; + unsigned int counter; + unsigned int status; + struct timer_list tl; + unsigned int MFlag; + struct BCState *bcs; + u_char *transbuf; + u_char *rcvbuf; + unsigned int transp; + unsigned int rcvp; + unsigned int transcnt; + unsigned int rcvcnt; + u_char IER; + u_char FCR; + u_char LCR; + u_char MCR; + u_char ctrl_reg; +}; + +struct teles3_hw { + unsigned int cfg_reg; + signed int isac; + signed int hscx[2]; + signed int isacfifo; + signed int hscxfifo[2]; +}; + +struct teles0_hw { unsigned int cfg_reg; unsigned int membase; +}; + +struct avm_hw { + unsigned int cfg_reg; unsigned int isac; unsigned int hscx[2]; + unsigned int isacfifo; + unsigned int hscxfifo[2]; unsigned int counter; +}; + +struct ix1_hw { + unsigned int cfg_reg; + unsigned int isac_ale; + unsigned int isac; + unsigned int hscx_ale; + unsigned int hscx; +}; + +struct diva_hw { + unsigned int cfg_reg; + unsigned int ctrl; + unsigned int isac_adr; + unsigned int isac; + unsigned int hscx_adr; + unsigned int hscx; + unsigned int status; + struct timer_list tl; + u_char ctrl_reg; +}; + +struct asus_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; + unsigned int u7; + unsigned int pots; +}; + + +struct hfc_hw { + unsigned int addr; + unsigned int fifosize; + unsigned char cirm; + unsigned char ctmt; + unsigned char cip; + u_char isac_spcr; + struct timer_list timer; +}; + +struct sedl_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; + unsigned int reset_on; + unsigned int reset_off; + struct isar_reg isar; + unsigned int chip; + unsigned int bus; +}; + +struct spt_hw { + unsigned int cfg_reg; + unsigned int isac; + unsigned int hscx[2]; + unsigned char res_irq; +}; + +struct mic_hw { + unsigned int cfg_reg; + unsigned int adr; + unsigned int isac; + unsigned int hscx; +}; + +struct njet_hw { + unsigned int base; + unsigned int isac; + unsigned int auxa; + unsigned char auxd; + unsigned char dmactrl; + unsigned char ctrl_reg; + unsigned char irqmask0; + unsigned char irqstat0; + unsigned char last_is0; +}; + +struct hfcD_hw { + unsigned int addr; + unsigned int bfifosize; + unsigned int dfifosize; + unsigned char cirm; + unsigned char ctmt; + unsigned char cip; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char int_s1; + unsigned char sctrl; + unsigned char stat; + unsigned char fifo; + unsigned char f1; + unsigned char f2; + unsigned int *send; + struct timer_list timer; +}; + +#define HW_IOM1 0 +#define HW_IPAC 1 +#define HW_ISAR 2 +#define FLG_TWO_DCHAN 4 +#define FLG_L1_DBUSY 5 +#define FLG_DBUSY_TIMER 6 +#define FLG_LOCK_ATOMIC 7 +#define HW_MON0_RX_END 8 +#define HW_MON1_RX_END 9 +#define HW_MON0_TX_END 10 +#define HW_MON1_TX_END 11 + +struct IsdnCardState { + unsigned char typ; + unsigned char subtyp; + int protocol; + unsigned int irq; + int HW_Flags; + int *busy_flag; + union { + struct elsa_hw elsa; + struct teles0_hw teles0; + struct teles3_hw teles3; + struct avm_hw avm; + struct ix1_hw ix1; + struct diva_hw diva; + struct asus_hw asus; + struct hfc_hw hfc; + struct sedl_hw sedl; + struct spt_hw spt; + struct mic_hw mic; + struct njet_hw njet; + struct hfcD_hw hfcD; + struct ix1_hw niccy; + } hw; int myid; isdn_if iif; u_char *status_buf; u_char *status_read; u_char *status_write; u_char *status_end; - void (*ph_command) (struct IsdnCardState *, unsigned int); - void (*modehscx) (struct HscxState *, int, int); - void (*hscx_fill_fifo) (struct HscxState *); - void (*isac_fill_fifo) (struct IsdnCardState *); + u_char (*readisac) (struct IsdnCardState *, u_char); + void (*writeisac) (struct IsdnCardState *, u_char, u_char); + void (*readisacfifo) (struct IsdnCardState *, u_char *, int); + void (*writeisacfifo) (struct IsdnCardState *, u_char *, int); + u_char (*BC_Read_Reg) (struct IsdnCardState *, int, u_char); + void (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char); + void (*BC_Send_Data) (struct BCState *); + int (*cardmsg) (struct IsdnCardState *, int, void *); struct Channel channel[2]; + struct BCState bcs[2]; struct PStack *stlist; u_char *rcvbuf; int rcvidx; @@ -396,16 +722,19 @@ int tx_cnt; int event; struct tq_struct tqueue; - int ph_active; + struct timer_list dbusytimer; struct sk_buff_head rq, sq; /* D-channel queues */ - int cardnr; int ph_state; - struct PStack *teistack; - struct HscxState hs[2]; - int dlogflag; - char *dlogspace; + int cardnr; + char *dlog; int debug; - unsigned int CallFlags; + u_char *mon_tx; + u_char *mon_rx; + int mon_txp; + int mon_txc; + int mon_rxp; + u_char mocr; + void (*setstack_d) (struct PStack *, struct IsdnCardState *); }; #define MON0_RX 1 @@ -413,128 +742,354 @@ #define MON0_TX 4 #define MON1_TX 8 +#define HISAX_MAX_CARDS 8 + #define ISDN_CTYPE_16_0 1 #define ISDN_CTYPE_8_0 2 #define ISDN_CTYPE_16_3 3 #define ISDN_CTYPE_PNP 4 #define ISDN_CTYPE_A1 5 #define ISDN_CTYPE_ELSA 6 -#define ISDN_CTYPE_ELSA_QS1000 7 +#define ISDN_CTYPE_ELSA_PNP 7 #define ISDN_CTYPE_TELESPCMCIA 8 #define ISDN_CTYPE_IX1MICROR2 9 +#define ISDN_CTYPE_ELSA_PCMCIA 10 +#define ISDN_CTYPE_DIEHLDIVA 11 +#define ISDN_CTYPE_ASUSCOM 12 +#define ISDN_CTYPE_TELEINT 13 +#define ISDN_CTYPE_TELES3C 14 +#define ISDN_CTYPE_SEDLBAUER 15 +#define ISDN_CTYPE_SPORTSTER 16 +#define ISDN_CTYPE_MIC 17 +#define ISDN_CTYPE_ELSA_PCI 18 +#define ISDN_CTYPE_COMPAQ_ISA 19 +#define ISDN_CTYPE_NETJET 20 +#define ISDN_CTYPE_TELESPCI 21 +#define ISDN_CTYPE_SEDLBAUER_PCMCIA 22 +#define ISDN_CTYPE_AMD7930 23 +#define ISDN_CTYPE_NICCY 24 +#define ISDN_CTYPE_S0BOX 25 +#define ISDN_CTYPE_A1_PCMCIA 26 +#define ISDN_CTYPE_FRITZPCI 27 +#define ISDN_CTYPE_SEDLBAUER_FAX 28 + +#define ISDN_CTYPE_COUNT 28 -#define ISDN_CTYPE_COUNT 9 +#ifdef ISDN_CHIP_ISAC +#undef ISDN_CHIP_ISAC +#endif + +#ifndef __initfunc +#define __initfunc(__arginit) __arginit +#endif + +#ifndef __initdata +#define __initdata +#endif + +#define HISAX_INITFUNC(__arginit) __initfunc(__arginit) +#define HISAX_INITDATA __initdata #ifdef CONFIG_HISAX_16_0 #define CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_TELES0 0 #endif #ifdef CONFIG_HISAX_16_3 #define CARD_TELES3 (1<< ISDN_CTYPE_16_3) | (1<< ISDN_CTYPE_PNP) | \ - (1<< ISDN_CTYPE_TELESPCMCIA) + (1<< ISDN_CTYPE_TELESPCMCIA) | (1<< ISDN_CTYPE_COMPAQ_ISA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_TELES3 0 #endif +#ifdef CONFIG_HISAX_TELESPCI +#define CARD_TELESPCI (1<< ISDN_CTYPE_TELESPCI) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_TELESPCI 0 +#endif + #ifdef CONFIG_HISAX_AVM_A1 #define CARD_AVM_A1 (1<< ISDN_CTYPE_A1) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_AVM_A1 0 #endif -#ifdef CONFIG_HISAX_ELSA_PCC -#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000) +#ifdef CONFIG_HISAX_AVM_A1_PCMCIA +#define CARD_AVM_A1_PCMCIA (1<< ISDN_CTYPE_A1_PCMCIA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else -#define CARD_ELSA 0 +#define CARD_AVM_A1_PCMCIA 0 #endif -#ifdef CONFIG_HISAX_ELSA_PCMCIA -#if CARD_ELSA -#error "You can't use a ELSA ISA card and a ELSA PCMCIA card with the same driver" +#ifdef CONFIG_HISAX_FRITZPCI +#define CARD_FRITZPCI (1<< ISDN_CTYPE_FRITZPCI) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else -#undef CARD_ELSA -#define CARD_ELSA (1<< ISDN_CTYPE_ELSA_QS1000) +#define CARD_FRITZPCI 0 #endif + +#ifdef CONFIG_HISAX_ELSA +#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_PNP) | \ + (1<< ISDN_CTYPE_ELSA_PCMCIA) | (1<< ISDN_CTYPE_ELSA_PCI) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#undef HISAX_INITFUNC +#define HISAX_INITFUNC(__arginit) __arginit +#undef HISAX_INITDATA +#define HISAX_INITDATA +#else +#define CARD_ELSA 0 #endif + #ifdef CONFIG_HISAX_IX1MICROR2 #define CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif #else #define CARD_IX1MICROR2 0 #endif +#ifdef CONFIG_HISAX_DIEHLDIVA +#define CARD_DIEHLDIVA (1 << ISDN_CTYPE_DIEHLDIVA) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_DIEHLDIVA 0 +#endif + +#ifdef CONFIG_HISAX_ASUSCOM +#define CARD_ASUSCOM (1 << ISDN_CTYPE_ASUSCOM) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_ASUSCOM 0 +#endif + +#ifdef CONFIG_HISAX_TELEINT +#define CARD_TELEINT (1 << ISDN_CTYPE_TELEINT) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_TELEINT 0 +#endif + +#ifdef CONFIG_HISAX_SEDLBAUER +#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA) | ( 1 << ISDN_CTYPE_SEDLBAUER_FAX) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_SEDLBAUER 0 +#endif + +#ifdef CONFIG_HISAX_SPORTSTER +#define CARD_SPORTSTER (1 << ISDN_CTYPE_SPORTSTER) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_SPORTSTER 0 +#endif + +#ifdef CONFIG_HISAX_MIC +#define CARD_MIC (1 << ISDN_CTYPE_MIC) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_MIC 0 +#endif + +#ifdef CONFIG_HISAX_NETJET +#define CARD_NETJET (1 << ISDN_CTYPE_NETJET) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_NETJET 0 +#endif + +#ifdef CONFIG_HISAX_TELES3C +#define CARD_TELES3C (1<< ISDN_CTYPE_TELES3C) +#else +#define CARD_TELES3C 0 +#endif + +#ifdef CONFIG_HISAX_AMD7930 +#define CARD_AMD7930 (1 << ISDN_CTYPE_AMD7930) +#else +#define CARD_AMD7930 0 +#endif + +#ifdef CONFIG_HISAX_NICCY +#define CARD_NICCY (1 << ISDN_CTYPE_NICCY) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_NICCY 0 +#endif + +#ifdef CONFIG_HISAX_S0BOX +#define CARD_S0BOX (1 << ISDN_CTYPE_S0BOX) +#ifndef ISDN_CHIP_ISAC +#define ISDN_CHIP_ISAC 1 +#endif +#else +#define CARD_S0BOX 0 +#endif + #define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \ - | CARD_IX1MICROR2) + | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \ + | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \ + | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \ + | CARD_AVM_A1_PCMCIA | CARD_FRITZPCI\ + | CARD_NICCY | CARD_S0BOX | CARD_TELESPCI) + +#define TEI_PER_CARD 0 + +#ifdef CONFIG_HISAX_1TR6 +#undef TEI_PER_CARD +#define TEI_PER_CARD 1 +#endif + +#ifdef CONFIG_HISAX_EURO +#undef TEI_PER_CARD +#define TEI_PER_CARD 1 +#define HISAX_EURO_SENDCOMPLETE 1 +#define EXT_BEARER_CAPS 1 +#define HISAX_SEND_STD_LLC_IE 1 +#ifdef CONFIG_HISAX_NO_SENDCOMPLETE +#undef HISAX_EURO_SENDCOMPLETE +#endif +#ifdef CONFIG_HISAX_NO_LLC +#undef HISAX_SEND_STD_LLC_IE +#endif +#undef HISAX_DE_AOC +#ifdef CONFIG_DE_AOC +#define HISAX_DE_AOC 1 +#endif +#endif + +/* L1 Debug */ +#define L1_DEB_WARN 0x01 +#define L1_DEB_INTSTAT 0x02 +#define L1_DEB_ISAC 0x04 +#define L1_DEB_ISAC_FIFO 0x08 +#define L1_DEB_HSCX 0x10 +#define L1_DEB_HSCX_FIFO 0x20 +#define L1_DEB_LAPD 0x40 +#define L1_DEB_IPAC 0x80 +#define L1_DEB_RECEIVE_FRAME 0x100 +#define L1_DEB_MONITOR 0x200 +#define DEB_DLOG_HEX 0x400 +#define DEB_DLOG_VERBOSE 0x800 + +#define L2FRAME_DEBUG + +#ifdef L2FRAME_DEBUG +extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir); +#endif struct IsdnCard { int typ; int protocol; /* EDSS1 or 1TR6 */ - unsigned int para[3]; - struct IsdnCardState *sp; + unsigned int para[4]; + struct IsdnCardState *cs; }; +void init_bcstate(struct IsdnCardState *cs, int bc); -#define LAPD 0 -#define LAPB 1 +void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs); +unsigned int random_ri(void); +void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st); +void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st); -void l2down(struct PStack *st, u_char pr, struct sk_buff *skb); -void l2up(struct PStack *st, u_char pr, struct sk_buff *skb); -void acceptph(struct PStack *st, struct sk_buff *skb); -void setstack_isdnl2(struct PStack *st, char *debug_id); -int HiSax_inithardware(void); -void HiSax_closehardware(void); +void setstack_l1_B(struct PStack *st); -void setstack_HiSax(struct PStack *st, struct IsdnCardState *sp); -unsigned int randomces(void); -void setstack_isdnl3(struct PStack *st, struct Channel *chanp); -void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st); +void setstack_tei(struct PStack *st); +void setstack_manager(struct PStack *st); + +void setstack_isdnl2(struct PStack *st, char *debug_id); void releasestack_isdnl2(struct PStack *st); +void setstack_transl2(struct PStack *st); +void releasestack_transl2(struct PStack *st); + +void setstack_l3dc(struct PStack *st, struct Channel *chanp); +void setstack_l3bc(struct PStack *st, struct Channel *chanp); void releasestack_isdnl3(struct PStack *st); -void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st); -void newcallref(struct PStack *st); -int setstack_hscx(struct PStack *st, struct HscxState *hs); u_char *findie(u_char * p, int size, u_char ie, int wanted_set); int getcallref(u_char * p); +int newcallref(void); void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount); void FsmFree(struct Fsm *fsm); int FsmEvent(struct FsmInst *fi, int event, void *arg); void FsmChangeState(struct FsmInst *fi, int newstate); void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft); -int FsmAddTimer(struct FsmTimer *ft, int millisec, - int event, void *arg, int where); +int FsmAddTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); +void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event, + void *arg, int where); void FsmDelTimer(struct FsmTimer *ft, int where); -int FsmTimerRunning(struct FsmTimer *ft); -void jiftime(char *s, long mark); +int jiftime(char *s, long mark); int HiSax_command(isdn_ctrl * ic); int HiSax_writebuf_skb(int id, int chan, struct sk_buff *skb); -void HiSax_putstatus(struct IsdnCardState *csta, char *buf); +void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...); +void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args); void HiSax_reportcard(int cardnr); int QuickHex(char *txt, u_char * p, int cnt); -void LogFrame(struct IsdnCardState *sp, u_char * p, int size); -void dlogframe(struct IsdnCardState *sp, u_char * p, int size, char *comment); +void LogFrame(struct IsdnCardState *cs, u_char * p, int size); +void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir); void iecpy(u_char * dest, u_char * iestart, int ieoffset); -void setstack_transl2(struct PStack *st); -void releasestack_transl2(struct PStack *st); -void close_hscxstate(struct HscxState *); -void setstack_tei(struct PStack *st); - -#endif /* __KERNEL__ */ +int discard_queue(struct sk_buff_head *q); +#ifdef ISDN_CHIP_ISAC +void setstack_isac(struct PStack *st, struct IsdnCardState *cs); +#endif /* ISDN_CHIP_ISAC */ +#endif /* __KERNEL__ */ #define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);} -int ll_run(struct IsdnCardState *csta); -void ll_stop(struct IsdnCardState *csta); +int ll_run(struct IsdnCardState *cs); +void ll_stop(struct IsdnCardState *cs); void CallcNew(void); void CallcFree(void); -int CallcNewChan(struct IsdnCardState *csta); -void CallcFreeChan(struct IsdnCardState *csta); +int CallcNewChan(struct IsdnCardState *cs); +void CallcFreeChan(struct IsdnCardState *cs); +void Isdnl1New(void); +void Isdnl1Free(void); void Isdnl2New(void); void Isdnl2Free(void); -void init_tei(struct IsdnCardState *sp, int protocol); -void release_tei(struct IsdnCardState *sp); +void Isdnl3New(void); +void Isdnl3Free(void); +void init_tei(struct IsdnCardState *cs, int protocol); +void release_tei(struct IsdnCardState *cs); char *HiSax_getrev(const char *revision); +void TeiNew(void); +void TeiFree(void); +int certification_check(int output); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hscx.c linux/drivers/isdn/hisax/hscx.c --- v2.0.35/linux/drivers/isdn/hisax/hscx.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hscx.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,319 @@ +/* $Id: hscx.c,v 1.3.2.11 1998/11/05 21:14:01 keil Exp $ + + * hscx.c HSCX specific routines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: hscx.c,v $ + * Revision 1.3.2.11 1998/11/05 21:14:01 keil + * minor fixes + * + * Revision 1.3.2.10 1998/11/03 00:06:37 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.3.2.9 1998/09/27 13:06:14 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.3.2.8 1998/09/15 15:25:04 keil + * Repair HSCX init + * + * Revision 1.3.2.7 1998/06/26 22:02:55 keil + * send flags between hdlc frames + * + * Revision 1.3.2.6 1998/06/09 18:26:32 keil + * PH_DEACTIVATE B-channel every time signaled to higher layer + * + * Revision 1.3.2.5 1998/05/27 18:05:34 keil + * HiSax 3.0 + * + * Revision 1.3.2.4 1998/04/08 21:57:04 keil + * Fix "lltrans ..." message + * New init code to fix problems during init if S0 is allready activ + * + * Revision 1.3.2.3 1997/11/27 12:30:55 keil + * cosmetic changes + * + * Revision 1.3.2.2 1997/11/15 18:54:25 keil + * cosmetics + * + * Revision 1.3.2.1 1997/10/17 22:10:44 keil + * new files on 2.0 + * + * Revision 1.3 1997/07/27 21:38:34 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:17 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hscx.h" +#include "isac.h" +#include "isdnl1.h" +#include + +static char *HSCXVer[] HISAX_INITDATA = +{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", + "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; + +HISAX_INITFUNC(int +HscxVersion(struct IsdnCardState *cs, char *s)) +{ + int verA, verB; + + verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf; + verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf; + printk(KERN_INFO "%s HSCX version A: %s B: %s\n", s, + HSCXVer[verA], HSCXVer[verB]); + if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) + return (1); + else + return (0); +} + +void +modehscx(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int hscx = bcs->hw.hscx.hscx; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hscx %c mode %d ichan %d", + 'A' + hscx, mode, bc); + bcs->mode = mode; + bcs->channel = bc; + cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF); + cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0); + cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0); + cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, + test_bit(HW_IPAC, &cs->HW_Flags) ? 0x82 : 0x85); + cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30); + cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7); + cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7); + + /* Switch IOM 1 SSI */ + if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0)) + bc = 1 - bc; + + if (bc == 0) { + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, + test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f); + } else { + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x3); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x3); + } + switch (mode) { + case (L1_MODE_NULL): + cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x1f); + cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x1f); + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84); + break; + case (L1_MODE_TRANS): + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4); + break; + case (L1_MODE_HDLC): + cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, + test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d); + cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c); + break; + } + if (mode) + cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41); + cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00); +} + +void +hscx_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +hscx_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hscx.count = 0; + restore_flags(flags); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->tx_skb = skb; + st->l1.bcs->hw.hscx.count = 0; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + modehscx(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + modehscx(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +void +close_hscxstate(struct BCState *bcs) +{ + modehscx(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.hscx.rcvbuf) { + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + } + if (bcs->blog) { + kfree(bcs->blog); + bcs->blog = NULL; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +int +open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hscx.rcvbuf\n"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + return (1); + } + if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for bcs->blog\n"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + return (2); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->hw.hscx.rcvidx = 0; + bcs->tx_cnt = 0; + return (0); +} + +int +setstack_hscx(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_hscxstate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hscx_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +HISAX_INITFUNC(void +clear_pending_hscx_ints(struct IsdnCardState *cs)) +{ + int val, eval; + + val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA); + debugl1(cs, "HSCX B ISTA %x", val); + if (val & 0x01) { + eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR); + debugl1(cs, "HSCX B EXIR %x", eval); + } + if (val & 0x02) { + eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR); + debugl1(cs, "HSCX A EXIR %x", eval); + } + val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA); + debugl1(cs, "HSCX A ISTA %x", val); + val = cs->BC_Read_Reg(cs, 1, HSCX_STAR); + debugl1(cs, "HSCX B STAR %x", val); + val = cs->BC_Read_Reg(cs, 0, HSCX_STAR); + debugl1(cs, "HSCX A STAR %x", val); + /* disable all IRQ */ + cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF); + cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF); +} + +HISAX_INITFUNC(void +inithscx(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_hscx; + cs->bcs[1].BC_SetStack = setstack_hscx; + cs->bcs[0].BC_Close = close_hscxstate; + cs->bcs[1].BC_Close = close_hscxstate; + cs->bcs[0].hw.hscx.hscx = 0; + cs->bcs[1].hw.hscx.hscx = 1; + modehscx(cs->bcs, 0, 0); + modehscx(cs->bcs + 1, 0, 0); +} + +HISAX_INITFUNC(void +inithscxisac(struct IsdnCardState *cs, int part)) +{ + if (part & 1) { + clear_pending_isac_ints(cs); + clear_pending_hscx_ints(cs); + initisac(cs); + inithscx(cs); + } + if (part & 2) { + /* Reenable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0); + cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0); + /* RESET Receiver and Transmitter */ + cs->writeisac(cs, ISAC_CMDR, 0x41); + } +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hscx.h linux/drivers/isdn/hisax/hscx.h --- v2.0.35/linux/drivers/isdn/hisax/hscx.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hscx.h Sun Nov 15 10:32:58 1998 @@ -0,0 +1,53 @@ +/* $Id: hscx.h,v 1.3.2.2 1998/04/08 21:57:30 keil Exp $ + + * hscx.h HSCX specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: hscx.h,v $ + * Revision 1.3.2.2 1998/04/08 21:57:30 keil + * New init code to fix problems during init if S0 is allready activ + * + * Revision 1.3.2.1 1997/10/17 22:10:45 keil + * new files on 2.0 + * + * Revision 1.3 1997/07/27 21:38:35 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:18 keil + * first version + * + * + */ + +/* All Registers original Siemens Spec */ + +#define HSCX_ISTA 0x20 +#define HSCX_CCR1 0x2f +#define HSCX_CCR2 0x2c +#define HSCX_TSAR 0x31 +#define HSCX_TSAX 0x30 +#define HSCX_XCCR 0x32 +#define HSCX_RCCR 0x33 +#define HSCX_MODE 0x22 +#define HSCX_CMDR 0x21 +#define HSCX_EXIR 0x24 +#define HSCX_XAD1 0x24 +#define HSCX_XAD2 0x25 +#define HSCX_RAH2 0x27 +#define HSCX_RSTA 0x27 +#define HSCX_TIMR 0x23 +#define HSCX_STAR 0x21 +#define HSCX_RBCL 0x25 +#define HSCX_XBCH 0x2d +#define HSCX_VSTR 0x2e +#define HSCX_RLCR 0x2e +#define HSCX_MASK 0x20 + +extern int HscxVersion(struct IsdnCardState *cs, char *s); +extern void hscx_sched_event(struct BCState *bcs, int event); +extern void modehscx(struct BCState *bcs, int mode, int bc); +extern void clear_pending_hscx_ints(struct IsdnCardState *cs); +extern void inithscx(struct IsdnCardState *cs); +extern void inithscxisac(struct IsdnCardState *cs, int part); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hscx_irq.c linux/drivers/isdn/hisax/hscx_irq.c --- v2.0.35/linux/drivers/isdn/hisax/hscx_irq.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/hscx_irq.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,308 @@ +/* $Id: hscx_irq.c,v 1.5.2.5 1998/11/03 00:06:39 keil Exp $ + + * hscx_irq.c low level b-channel stuff for Siemens HSCX + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This is an include file for fast inline IRQ stuff + * + * $Log: hscx_irq.c,v $ + * Revision 1.5.2.5 1998/11/03 00:06:39 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.5.2.4 1998/09/27 13:06:16 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.5.2.3 1998/06/24 14:43:56 keil + * Fix recovery of TX IRQ loss + * + * Revision 1.5.2.2 1998/05/27 18:05:36 keil + * HiSax 3.0 + * + * Revision 1.5.2.1 1997/10/17 22:10:46 keil + * new files on 2.0 + * + * Revision 1.4 1997/08/15 17:48:02 keil + * cosmetic + * + * Revision 1.3 1997/07/27 21:38:36 keil + * new B-channel interface + * + * Revision 1.2 1997/06/26 11:16:19 keil + * first version + * + * + */ + + +static inline void +waitforCEC(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC timeout\n"); +} + + +static inline void +waitforXFW(struct IsdnCardState *cs, int hscx) +{ + int to = 50; + + while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforXFW timeout\n"); +} + +static inline void +WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(cs, hscx); + WRITEHSCX(cs, hscx, HSCX_CMDR, data); + restore_flags(flags); +} + + + +static void +hscx_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct IsdnCardState *cs = bcs->cs; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_empty_fifo"); + + if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hscx_empty_fifo: incoming packet too large"); + WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); + bcs->hw.hscx.rcvidx = 0; + return; + } + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + bcs->hw.hscx.rcvidx += count; + save_flags(flags); + cli(); + READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count); + WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "hscx_empty_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static void +hscx_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int more, count; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + u_char *ptr; + long flags; + + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hscx_fill_fifo"); + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; + if (bcs->tx_skb->len > fifo_size) { + more = !0; + count = fifo_size; + } else + count = bcs->tx_skb->len; + + waitforXFW(cs, bcs->hw.hscx.hscx); + save_flags(flags); + cli(); + ptr = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hscx.count += count; + WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count); + WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "hscx_fill_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static inline void +hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx) +{ + u_char r; + struct BCState *bcs = cs->bcs + hscx; + struct sk_buff *skb; + int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32; + int count; + + if (!test_bit(BC_FLG_INIT, &bcs->Flag)) + return; + + if (val & 0x80) { /* RME */ + r = READHSCX(cs, hscx, HSCX_RSTA); + if ((r & 0xf0) != 0xa0) { + if (!(r & 0x80)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX invalid frame"); + if ((r & 0x40) && bcs->mode) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX RDO mode=%d", + bcs->mode); + if (!(r & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX CRC error"); + WriteHSCXCMDR(cs, hscx, 0x80); + } else { + count = READHSCX(cs, hscx, HSCX_RBCL) & ( + test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f); + if (count == 0) + count = fifo_size; + hscx_empty_fifo(bcs, count); + if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { + if (cs->debug & L1_DEB_HSCX_FIFO) + debugl1(cs, "HX Frame %d", count); + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "HSCX: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + } + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + hscx_empty_fifo(bcs, fifo_size); + if (bcs->mode == L1_MODE_TRANS) { + /* receive audio data */ + if (!(skb = dev_alloc_skb(fifo_size))) + printk(KERN_WARNING "HiSax: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->hw.hscx.rcvidx = 0; + hscx_sched_event(bcs, B_RCVBUFREADY); + } + } + if (val & 0x10) { /* XPR */ + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + hscx_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->hw.hscx.count = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + hscx_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +static inline void +hscx_int_main(struct IsdnCardState *cs, u_char val) +{ + + u_char exval; + struct BCState *bcs; + + if (val & 0x01) { + bcs = cs->bcs + 1; + exval = READHSCX(cs, 1, HSCX_EXIR); + if (exval & 0x40) { + if (bcs->mode == 1) + hscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX B EXIR %x Lost TX", exval); + } + } else if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX B EXIR %x", exval); + } + if (val & 0xf8) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX B interrupt %x", val); + hscx_interrupt(cs, val, 1); + } + if (val & 0x02) { + bcs = cs->bcs; + exval = READHSCX(cs, 0, HSCX_EXIR); + if (exval & 0x40) { + if (bcs->mode == L1_MODE_TRANS) + hscx_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "HSCX A EXIR %x Lost TX", exval); + } + } else if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX A EXIR %x", exval); + } + if (val & 0x04) { + exval = READHSCX(cs, 0, HSCX_ISTA); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX A interrupt %x", exval); + hscx_interrupt(cs, exval, 0); + } +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/ipac.h linux/drivers/isdn/hisax/ipac.h --- v2.0.35/linux/drivers/isdn/hisax/ipac.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/ipac.h Sun Nov 15 10:32:58 1998 @@ -0,0 +1,36 @@ +/* $Id: ipac.h,v 1.1.2.2 1998/04/11 18:49:48 keil Exp $ + + * ipac.h IPAC specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: ipac.h,v $ + * Revision 1.1.2.2 1998/04/11 18:49:48 keil + * add IPAC_ATX + * + * Revision 1.1.2.1 1997/10/17 22:10:48 keil + * new files on 2.0 + * + * + * + */ + + +/* All Registers original Siemens Spec */ + +#define IPAC_CONF 0xC0 +#define IPAC_MASK 0xC1 +#define IPAC_ISTA 0xC1 +#define IPAC_ID 0xC2 +#define IPAC_ACFG 0xC3 +#define IPAC_AOE 0xC4 +#define IPAC_ARX 0xC5 +#define IPAC_ATX 0xC5 +#define IPAC_PITA1 0xC6 +#define IPAC_PITA2 0xC7 +#define IPAC_POTA1 0xC8 +#define IPAC_POTA2 0xC9 +#define IPAC_PCFG 0xCA +#define IPAC_SCFG 0xCB +#define IPAC_TIMR2 0xCC diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isac.c linux/drivers/isdn/hisax/isac.c --- v2.0.35/linux/drivers/isdn/hisax/isac.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isac.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,669 @@ +/* $Id: isac.c,v 1.7.2.9 1998/11/03 00:06:41 keil Exp $ + + * isac.c ISAC specific routines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * + * $Log: isac.c,v $ + * Revision 1.7.2.9 1998/11/03 00:06:41 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.7.2.8 1998/09/27 13:06:18 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.7.2.7 1998/05/27 18:05:38 keil + * HiSax 3.0 + * + * Revision 1.7.2.6 1998/04/08 21:57:31 keil + * New init code to fix problems during init if S0 is allready activ + * + * Revision 1.7.2.5 1998/03/07 23:15:24 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.7.2.4 1998/02/09 11:24:06 keil + * New leased line support (Read README.HiSax!) + * + * Revision 1.7.2.3 1998/01/11 22:58:55 keil + * new setstack interface + * + * Revision 1.7.2.2 1997/11/15 18:54:23 keil + * cosmetics + * + * Revision 1.7.2.1 1997/10/17 22:10:49 keil + * new files on 2.0 + * + * Revision 1.6 1997/08/15 17:47:08 keil + * avoid oops because a uninitialised timer + * + * Revision 1.5 1997/08/07 17:48:49 keil + * fix wrong parenthesis + * + * Revision 1.4 1997/07/30 17:11:59 keil + * fixed Timer3 + * + * Revision 1.3 1997/07/27 21:37:40 keil + * T3 implemented; supervisor l1timer; B-channel TEST_LOOP + * + * Revision 1.2 1997/06/26 11:16:15 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "isdnl1.h" +#include + +#define DBUSY_TIMER_VALUE 80 +#define ARCOFI_USE 1 + +static char *ISACVer[] HISAX_INITDATA = +{"2086/2186 V1.1", "2085 B1", "2085 B2", + "2085 V2.3"}; + +void +ISACVersion(struct IsdnCardState *cs, char *s) +{ + int val; + + val = cs->readisac(cs, ISAC_RBCH); + printk(KERN_INFO "%s ISAC version (%x): %s\n", s, val, ISACVer[(val >> 5) & 3]); +} + +static void +ph_command(struct IsdnCardState *cs, unsigned int command) +{ + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_command %x", command); + cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3); +} + + +static void +isac_new_ph(struct IsdnCardState *cs) +{ + switch (cs->ph_state) { + case (ISAC_IND_RS): + case (ISAC_IND_EI): + ph_command(cs, ISAC_CMD_DUI); + l1_msg(cs, HW_RESET | INDICATION, NULL); + break; + case (ISAC_IND_DID): + l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL); + break; + case (ISAC_IND_DR): + l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); + break; + case (ISAC_IND_PU): + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (ISAC_IND_RSY): + l1_msg(cs, HW_RSYNC | INDICATION, NULL); + break; + case (ISAC_IND_ARD): + l1_msg(cs, HW_INFO2 | INDICATION, NULL); + break; + case (ISAC_IND_AI8): + l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); + break; + case (ISAC_IND_AI10): + l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL); + break; + default: + break; + } +} + +static void +isac_bh(struct IsdnCardState *cs) +{ + struct PStack *stptr; + + if (!cs) + return; + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) + isac_new_ph(cs); + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); + if (test_and_clear_bit(D_RX_MON0, &cs->event)) + test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_RX_MON1, &cs->event)) + test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_TX_MON0, &cs->event)) + test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + if (test_and_clear_bit(D_TX_MON1, &cs->event)) + test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); +} + +void +isac_empty_fifo(struct IsdnCardState *cs, int count) +{ + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "isac_empty_fifo"); + + if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isac_empty_fifo overrun %d", + cs->rcvidx + count); + cs->writeisac(cs, ISAC_CMDR, 0x80); + cs->rcvidx = 0; + return; + } + ptr = cs->rcvbuf + cs->rcvidx; + cs->rcvidx += count; + save_flags(flags); + cli(); + cs->readisacfifo(cs, ptr, count); + cs->writeisac(cs, ISAC_CMDR, 0x80); + restore_flags(flags); + if (cs->debug & L1_DEB_ISAC_FIFO) { + char *t = cs->dlog; + + t += sprintf(t, "isac_empty_fifo cnt %d", count); + QuickHex(t, ptr, count); + debugl1(cs, cs->dlog); + } +} + +static void +isac_fill_fifo(struct IsdnCardState *cs) +{ + int count, more; + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) + debugl1(cs, "isac_fill_fifo"); + + if (!cs->tx_skb) + return; + + count = cs->tx_skb->len; + if (count <= 0) + return; + + more = 0; + if (count > 32) { + more = !0; + count = 32; + } + save_flags(flags); + cli(); + ptr = cs->tx_skb->data; + skb_pull(cs->tx_skb, count); + 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); + } + init_timer(&cs->dbusytimer); + cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000); + add_timer(&cs->dbusytimer); + if (cs->debug & L1_DEB_ISAC_FIFO) { + char *t = cs->dlog; + + t += sprintf(t, "isac_fill_fifo cnt %d", count); + QuickHex(t, ptr, count); + debugl1(cs, cs->dlog); + } +} + +void +isac_sched_event(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +void +isac_interrupt(struct IsdnCardState *cs, u_char val) +{ + u_char exval, v1; + struct sk_buff *skb; + unsigned int count; + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC interrupt %x", val); + if (val & 0x80) { /* RME */ + exval = cs->readisac(cs, ISAC_RSTA); + if ((exval & 0x70) != 0x20) { + if (exval & 0x40) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC RDO"); + if (!(exval & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC CRC error"); + cs->writeisac(cs, ISAC_CMDR, 0x80); + } else { + count = cs->readisac(cs, ISAC_RBCL) & 0x1f; + if (count == 0) + count = 32; + isac_empty_fifo(cs, count); + save_flags(flags); + cli(); + if ((count = cs->rcvidx) > 0) { + cs->rcvidx = 0; + if (!(skb = alloc_skb(count, GFP_ATOMIC))) + printk(KERN_WARNING "HiSax: D receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), cs->rcvbuf, count); + skb_queue_tail(&cs->rq, skb); + } + } + restore_flags(flags); + } + cs->rcvidx = 0; + isac_sched_event(cs, D_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + isac_empty_fifo(cs, 32); + } + if (val & 0x20) { /* RSC */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC RSC interrupt"); + } + if (val & 0x10) { /* XPR */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + isac_sched_event(cs, D_CLEARBUSY); + if (cs->tx_skb) { + if (cs->tx_skb->len) { + isac_fill_fifo(cs); + goto afterXPR; + } else { + dev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + isac_fill_fifo(cs); + } else + isac_sched_event(cs, D_XMTBUFREADY); + } + afterXPR: + if (val & 0x04) { /* CISQ */ + exval = cs->readisac(cs, ISAC_CIR0); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC CIR0 %02X", exval ); + if (exval & 2) { + cs->ph_state = (exval >> 2) & 0xf; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_state change %x", cs->ph_state); + isac_sched_event(cs, D_L1STATECHANGE); + } + if (exval & 1) { + exval = cs->readisac(cs, ISAC_CIR1); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC CIR1 %02X", exval ); + } + } + if (val & 0x02) { /* SIN */ + /* never */ + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC SIN interrupt"); + } + if (val & 0x01) { /* EXI */ + exval = cs->readisac(cs, ISAC_EXIR); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC EXIR %02x", exval); + if (exval & 0x04) { + v1 = cs->readisac(cs, ISAC_MOSR); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ISAC MOSR %02x", v1); +#if ARCOFI_USE + if (v1 & 0x08) { + if (!cs->mon_rx) { + if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX out of memory!"); + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto afterMONR0; + } else + cs->mon_rxp = 0; + } + if (cs->mon_rxp >= MAX_MON_FRAME) { + cs->mocr &= 0xf0; + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mon_rxp = 0; + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX overflow!"); + goto afterMONR0; + } + cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR0); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]); + if (cs->mon_rxp == 1) { + cs->mocr |= 0x04; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + } + } + afterMONR0: + if (v1 & 0x80) { + if (!cs->mon_rx) { + if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX out of memory!"); + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + goto afterMONR1; + } else + cs->mon_rxp = 0; + } + if (cs->mon_rxp >= MAX_MON_FRAME) { + cs->mocr &= 0x0f; + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mon_rxp = 0; + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "ISAC MON RX overflow!"); + goto afterMONR1; + } + cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR1); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]); + cs->mocr |= 0x40; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + } + afterMONR1: + if (v1 & 0x04) { + cs->mocr &= 0xf0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags); + } + if (v1 & 0x40) { + cs->mocr &= 0x0f; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags); + } + if (v1 & 0x02) { + if ((!cs->mon_tx) || (cs->mon_txc && + (cs->mon_txp >= cs->mon_txc) && + !(v1 & 0x08))) { + cs->mocr &= 0xf0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mocr |= 0x0a; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + if (cs->mon_txc && + (cs->mon_txp >= cs->mon_txc)) + test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + goto AfterMOX0; + } + if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) { + test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags); + goto AfterMOX0; + } + cs->writeisac(cs, ISAC_MOX0, + cs->mon_tx[cs->mon_txp++]); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]); + } + AfterMOX0: + if (v1 & 0x20) { + if ((!cs->mon_tx) || (cs->mon_txc && + (cs->mon_txp >= cs->mon_txc) && + !(v1 & 0x80))) { + cs->mocr &= 0x0f; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + cs->mocr |= 0xa0; + cs->writeisac(cs, ISAC_MOCR, cs->mocr); + if (cs->mon_txc && + (cs->mon_txp >= cs->mon_txc)) + test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + goto AfterMOX1; + } + if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) { + test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags); + goto AfterMOX1; + } + cs->writeisac(cs, ISAC_MOX1, + cs->mon_tx[cs->mon_txp++]); + if (cs->debug & L1_DEB_MONITOR) + debugl1(cs, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]); + } + AfterMOX1: +#endif + } + } +} + +static void +ISAC_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + int val; + + switch (pr) { + case (PH_DATA |REQUEST): + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + if (cs->tx_skb) { + skb_queue_tail(&cs->sq, skb); +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA Queued", 0); +#endif + } else { + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 0); +#endif + isac_fill_fifo(cs); + } + break; + case (PH_PULL |INDICATION): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA_PULLED", 0); +#endif + isac_fill_fifo(cs); + break; + case (PH_PULL | REQUEST): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (HW_RESET | REQUEST): + if ((cs->ph_state == ISAC_IND_EI) || + (cs->ph_state == ISAC_IND_DR) || + (cs->ph_state == ISAC_IND_RS)) + ph_command(cs, ISAC_CMD_TIM); + else + ph_command(cs, ISAC_CMD_RS); + break; + case (HW_ENABLE | REQUEST): + ph_command(cs, ISAC_CMD_TIM); + break; + case (HW_INFO3 | REQUEST): + ph_command(cs, ISAC_CMD_AR8); + break; + case (HW_TESTLOOP | REQUEST): + val = 0; + if (1 & (long) arg) + val |= 0x0c; + if (2 & (long) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; + case (HW_DEACTIVATE | RESPONSE): + discard_queue(&cs->rq); + discard_queue(&cs->sq); + if (cs->tx_skb) { + dev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_skb = NULL; + } + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + isac_sched_event(cs, D_CLEARBUSY); + break; + default: + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isac_l1hw unknown %04x", pr); + break; + } +} + +void +setstack_isac(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = ISAC_l1hw; +} + +static void +dbusy_timer_handler(struct IsdnCardState *cs) +{ + struct PStack *stptr; + int val; + + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) { + debugl1(cs, "D-Channel Busy"); + val = cs->readisac(cs, ISAC_RBCH); + if (val & ISAC_RBCH_XAC) + debugl1(cs, "ISAC XAC"); + else + debugl1(cs, "ISAC No XAC"); + } + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + stptr = stptr->next; + } + } +} + +HISAX_INITFUNC(void +initisac(struct IsdnCardState *cs)) +{ + cs->tqueue.routine = (void *) (void *) isac_bh; + cs->setstack_d = setstack_isac; + cs->dbusytimer.function = (void *) dbusy_timer_handler; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->writeisac(cs, ISAC_MASK, 0xff); + cs->mocr = 0xaa; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + cs->writeisac(cs, ISAC_ADF2, 0x0); + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + cs->writeisac(cs, ISAC_STCR, 0x70); + cs->writeisac(cs, ISAC_MODE, 0xc9); + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_ADF2, 0x80); + cs->writeisac(cs, ISAC_SQXR, 0x2f); + cs->writeisac(cs, ISAC_SPCR, 0x00); + cs->writeisac(cs, ISAC_STCR, 0x70); + cs->writeisac(cs, ISAC_MODE, 0xc9); + cs->writeisac(cs, ISAC_TIMR, 0x00); + cs->writeisac(cs, ISAC_ADF1, 0x00); + } + ph_command(cs, ISAC_CMD_RS); + cs->writeisac(cs, ISAC_MASK, 0x0); +} + +HISAX_INITFUNC(void +clear_pending_isac_ints(struct IsdnCardState *cs)) +{ + int val, eval; + + val = cs->readisac(cs, ISAC_STAR); + debugl1(cs, "ISAC STAR %x", val); + val = cs->readisac(cs, ISAC_MODE); + debugl1(cs, "ISAC MODE %x", val); + val = cs->readisac(cs, ISAC_ADF2); + debugl1(cs, "ISAC ADF2 %x", val); + val = cs->readisac(cs, ISAC_ISTA); + debugl1(cs, "ISAC ISTA %x", val); + if (val & 0x01) { + eval = cs->readisac(cs, ISAC_EXIR); + debugl1(cs, "ISAC EXIR %x", eval); + } + val = cs->readisac(cs, ISAC_CIR0); + debugl1(cs, "ISAC CIR0 %x", val); + cs->ph_state = (val >> 2) & 0xf; + isac_sched_event(cs, D_L1STATECHANGE); + /* Disable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0xFF); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isac.h linux/drivers/isdn/hisax/isac.h --- v2.0.35/linux/drivers/isdn/hisax/isac.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isac.h Sun Nov 15 10:32:58 1998 @@ -0,0 +1,84 @@ +/* $Id: isac.h,v 1.3.2.3 1998/05/27 18:05:41 keil Exp $ + + * isac.h ISAC specific defines + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: isac.h,v $ + * Revision 1.3.2.3 1998/05/27 18:05:41 keil + * HiSax 3.0 + * + * Revision 1.3.2.2 1997/11/15 19:01:14 keil + * ipac changes + * + * Revision 1.3.2.1 1997/10/17 22:10:50 keil + * new files on 2.0 + * + * Revision 1.3 1997/07/27 21:37:41 keil + * T3 implemented; supervisor l1timer; B-channel TEST_LOOP + * + * Revision 1.2 1997/06/26 11:16:16 keil + * first version + * + * + */ + + +/* All Registers original Siemens Spec */ + +#define ISAC_MASK 0x20 +#define ISAC_ISTA 0x20 +#define ISAC_STAR 0x21 +#define ISAC_CMDR 0x21 +#define ISAC_EXIR 0x24 +#define ISAC_ADF2 0x39 +#define ISAC_SPCR 0x30 +#define ISAC_ADF1 0x38 +#define ISAC_CIR0 0x31 +#define ISAC_CIX0 0x31 +#define ISAC_CIR1 0x33 +#define ISAC_CIX1 0x33 +#define ISAC_STCR 0x37 +#define ISAC_MODE 0x22 +#define ISAC_RSTA 0x27 +#define ISAC_RBCL 0x25 +#define ISAC_RBCH 0x2A +#define ISAC_TIMR 0x23 +#define ISAC_SQXR 0x3b +#define ISAC_MOSR 0x3a +#define ISAC_MOCR 0x3a +#define ISAC_MOR0 0x32 +#define ISAC_MOX0 0x32 +#define ISAC_MOR1 0x34 +#define ISAC_MOX1 0x34 + +#define ISAC_RBCH_XAC 0x80 + +#define ISAC_CMD_TIM 0x0 +#define ISAC_CMD_RS 0x1 +#define ISAC_CMD_SCZ 0x4 +#define ISAC_CMD_SSZ 0x2 +#define ISAC_CMD_AR8 0x8 +#define ISAC_CMD_AR10 0x9 +#define ISAC_CMD_ARL 0xA +#define ISAC_CMD_DUI 0xF + +#define ISAC_IND_RS 0x1 +#define ISAC_IND_PU 0x7 +#define ISAC_IND_DR 0x0 +#define ISAC_IND_SD 0x2 +#define ISAC_IND_DIS 0x3 +#define ISAC_IND_EI 0x6 +#define ISAC_IND_RSY 0x4 +#define ISAC_IND_ARD 0x8 +#define ISAC_IND_TI 0xA +#define ISAC_IND_ATI 0xB +#define ISAC_IND_AI8 0xC +#define ISAC_IND_AI10 0xD +#define ISAC_IND_DID 0xF + +extern void ISACVersion(struct IsdnCardState *cs, char *s); +extern void initisac(struct IsdnCardState *cs); +extern void isac_interrupt(struct IsdnCardState *cs, u_char val); +extern void clear_pending_isac_ints(struct IsdnCardState *cs); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c --- v2.0.35/linux/drivers/isdn/hisax/isar.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isar.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,949 @@ +/* $Id: isar.c,v 1.1.2.4 1998/11/03 00:06:44 keil Exp $ + + * isar.c ISAR (Siemens PSB 7110) specific routines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: isar.c,v $ + * Revision 1.1.2.4 1998/11/03 00:06:44 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.3 1998/10/04 23:04:58 keil + * ISAR works now + * + * Revision 1.1.2.2 1998/09/30 22:28:07 keil + * more work for isar support + * + * Revision 1.1.2.1 1998/09/27 13:01:43 keil + * Start support for ISAR based cards + * + * Revision 1.1 1998/08/13 23:33:47 keil + * First version, only init + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isar.h" +#include "isdnl1.h" +#include + +#define DBG_LOADFIRM 0 +#define DUMP_MBOXFRAME 2 + +#define MIN(a,b) ((aBC_Read_Reg(cs, 0, ISAR_HIA) & 1) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) + printk(KERN_WARNING "HiSax: ISAR waitforHIA timeout\n"); + return(timeout); +} + + +int +sendmsg(struct IsdnCardState *cs, u_char his, u_char creg, u_char len, + u_char *msg) +{ + long flags; + int i; + + if (!waitforHIA(cs, 4000)) + return(0); +#if DUMP_MBOXFRAME + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "sendmsg(%02x,%02x,%d)", his, creg, len); +#endif + save_flags(flags); + cli(); + cs->BC_Write_Reg(cs, 0, ISAR_CTRL_H, creg); + cs->BC_Write_Reg(cs, 0, ISAR_CTRL_L, len); + cs->BC_Write_Reg(cs, 0, ISAR_WADR, 0); + if (msg && len) { + cs->BC_Write_Reg(cs, 1, ISAR_MBOX, msg[0]); + for (i=1; iBC_Write_Reg(cs, 2, ISAR_MBOX, msg[i]); +#if DUMP_MBOXFRAME>1 + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[256], *t; + + i = len; + while (i>0) { + t = tmp; + t += sprintf(t, "sendmbox cnt %d", len); + QuickHex(t, &msg[len-i], (i>64) ? 64:i); + debugl1(cs, tmp); + i -= 64; + } + } +#endif + } + cs->BC_Write_Reg(cs, 1, ISAR_HIS, his); + restore_flags(flags); + waitforHIA(cs, 10000); + return(1); +} + +/* Call only with IRQ disabled !!! */ +inline void +rcv_mbox(struct IsdnCardState *cs, struct isar_reg *ireg, u_char *msg) +{ + int i; + + cs->BC_Write_Reg(cs, 1, ISAR_RADR, 0); + if (msg && ireg->clsb) { + msg[0] = cs->BC_Read_Reg(cs, 1, ISAR_MBOX); + for (i=1; i < ireg->clsb; i++) + msg[i] = cs->BC_Read_Reg(cs, 2, ISAR_MBOX); +#if DUMP_MBOXFRAME>1 + if (cs->debug & L1_DEB_HSCX_FIFO) { + char tmp[256], *t; + + i = ireg->clsb; + while (i>0) { + t = tmp; + t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb); + QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i); + debugl1(cs, tmp); + i -= 64; + } + } +#endif + } + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); +} + +/* Call only with IRQ disabled !!! */ +inline void +get_irq_infos(struct IsdnCardState *cs, struct isar_reg *ireg) +{ + ireg->iis = cs->BC_Read_Reg(cs, 1, ISAR_IIS); + ireg->cmsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_H); + ireg->clsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_L); +#if DUMP_MBOXFRAME + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "rcv_mbox(%02x,%02x,%d)", ireg->iis, ireg->cmsb, + ireg->clsb); +#endif +} + +int +waitrecmsg(struct IsdnCardState *cs, u_char *len, + u_char *msg, int maxdelay) +{ + int timeout = 0; + long flags; + struct isar_reg *ir = cs->bcs[0].hw.isar.reg; + + + while((!(cs->BC_Read_Reg(cs, 0, ISAR_IRQBIT) & ISAR_IRQSTA)) && + (timeout++ < maxdelay)) + udelay(1); + if (timeout >= maxdelay) { + printk(KERN_WARNING"isar recmsg IRQSTA timeout\n"); + return(0); + } + save_flags(flags); + cli(); + get_irq_infos(cs, ir); + rcv_mbox(cs, ir, msg); + *len = ir->clsb; + restore_flags(flags); + return(1); +} + +int +ISARVersion(struct IsdnCardState *cs, char *s) +{ + int ver; + u_char msg[] = ISAR_MSG_HWVER; + u_char tmp[64]; + u_char len; + int debug; + + cs->cardmsg(cs, CARD_RESET, NULL); + /* disable ISAR IRQ */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0); + debug = cs->debug; + cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO); + if (!sendmsg(cs, ISAR_HIS_VNR, 0, 3, msg)) + return(-1); + if (!waitrecmsg(cs, &len, tmp, 100000)) + return(-2); + cs->debug = debug; + if (cs->bcs[0].hw.isar.reg->iis == ISAR_IIS_VNR) { + if (len == 1) { + ver = tmp[0] & 0xf; + printk(KERN_INFO "%s ISAR version %d\n", s, ver); + return(ver); + } + return(-3); + } + return(-4); +} + +int +isar_load_firmware(struct IsdnCardState *cs, u_char *buf) +{ + int ret, size, cnt, debug; + u_char len, nom, noc; + u_short sadr, left, *sp; + u_char *p = buf; + u_char *msg, *tmpmsg, *mp, tmp[64]; + long flags; + struct isar_reg *ireg = cs->bcs[0].hw.isar.reg; + + struct {u_short sadr; + u_short len; + u_short d_key; + } blk_head; + +#define BLK_HEAD_SIZE 6 + if (1 != (ret = ISARVersion(cs, "Testing"))) { + printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret); + return(1); + } + debug = cs->debug; +#if DBG_LOADFIRM<2 + cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO); +#endif + printk(KERN_DEBUG"isar_load_firmware buf %#lx\n", (u_long)buf); + if ((ret = verify_area(VERIFY_READ, (void *) p, sizeof(int)))) { + printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret); + return ret; + } + if ((ret = copy_from_user(&size, p, sizeof(int)))) { + printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret); + return ret; + } + p += sizeof(int); + printk(KERN_DEBUG"isar_load_firmware size: %d\n", size); + if ((ret = verify_area(VERIFY_READ, (void *) p, size))) { + printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret); + return ret; + } + cnt = 0; + /* disable ISAR IRQ */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0); + if (!(msg = kmalloc(256, GFP_KERNEL))) { + printk(KERN_ERR"isar_load_firmware no buffer\n"); + return (1); + } + if (!(tmpmsg = kmalloc(256, GFP_KERNEL))) { + printk(KERN_ERR"isar_load_firmware no tmp buffer\n"); + kfree(msg); + return (1); + } + while (cnt < size) { + if ((ret = copy_from_user(&blk_head, p, BLK_HEAD_SIZE))) { + printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret); + goto reterror; + } + cnt += BLK_HEAD_SIZE; + p += BLK_HEAD_SIZE; + printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n", + blk_head.sadr, blk_head.len, blk_head.d_key & 0xff); + sadr = blk_head.sadr; + left = blk_head.len; + if (!sendmsg(cs, ISAR_HIS_DKEY, blk_head.d_key & 0xff, 0, NULL)) { + printk(KERN_ERR"isar sendmsg dkey failed\n"); + ret = 1;goto reterror; + } + if (!waitrecmsg(cs, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg dkey failed\n"); + ret = 1;goto reterror; + } + if ((ireg->iis != ISAR_IIS_DKEY) || ireg->cmsb || len) { + printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n", + ireg->iis, ireg->cmsb, len); + ret = 1;goto reterror; + } + while (left>0) { + noc = MIN(126, left); + nom = 2*noc; + mp = msg; + *mp++ = sadr / 256; + *mp++ = sadr % 256; + left -= noc; + *mp++ = noc; + if ((ret = copy_from_user(tmpmsg, p, nom))) { + printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret); + goto reterror; + } + p += nom; + cnt += nom; + nom += 3; + sp = (u_short *)tmpmsg; +#if DBG_LOADFIRM + printk(KERN_DEBUG"isar: load %3d words at %04x\n", + noc, sadr); +#endif + sadr += noc; + while(noc) { + *mp++ = *sp / 256; + *mp++ = *sp % 256; + sp++; + noc--; + } + if (!sendmsg(cs, ISAR_HIS_FIRM, 0, nom, msg)) { + printk(KERN_ERR"isar sendmsg prog failed\n"); + ret = 1;goto reterror; + } + if (!waitrecmsg(cs, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg prog failed\n"); + ret = 1;goto reterror; + } + if ((ireg->iis != ISAR_IIS_FIRM) || ireg->cmsb || len) { + printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n", + ireg->iis, ireg->cmsb, len); + ret = 1;goto reterror; + } + } + printk(KERN_DEBUG"isar firmware block %5d words loaded\n", + blk_head.len); + } + msg[0] = 0xff; + msg[1] = 0xfe; + ireg->bstat = 0; + if (!sendmsg(cs, ISAR_HIS_STDSP, 0, 2, msg)) { + printk(KERN_ERR"isar sendmsg start dsp failed\n"); + ret = 1;goto reterror; + } + if (!waitrecmsg(cs, &len, tmp, 100000)) { + printk(KERN_ERR"isar waitrecmsg start dsp failed\n"); + ret = 1;goto reterror; + } + if ((ireg->iis != ISAR_IIS_STDSP) || ireg->cmsb || len) { + printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n", + ireg->iis, ireg->cmsb, len); + ret = 1;goto reterror; + } else + printk(KERN_DEBUG"isar start dsp success\n"); + /* NORMAL mode entered */ + /* Enable IRQs of ISAR */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, ISAR_IRQSTA); + save_flags(flags); + sti(); + cnt = 1000; /* max 1s */ + while ((!ireg->bstat) && cnt) { + udelay(1000); + cnt--; + } + if (!cnt) { + printk(KERN_ERR"isar no general status event received\n"); + ret = 1;goto reterrflg; + } else { + printk(KERN_DEBUG"isar general status event %x\n", + ireg->bstat); + } + ireg->iis = 0; + if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) { + printk(KERN_ERR"isar sendmsg self tst failed\n"); + ret = 1;goto reterrflg; + } + cnt = 1000; /* max 10 ms */ + while ((ireg->iis != ISAR_IIS_DIAG) && cnt) { + udelay(10); + cnt--; + } + if (!cnt) { + printk(KERN_ERR"isar no self tst response\n"); + ret = 1;goto reterrflg; + } else if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1) + && (ireg->par[0] == 0)) { + printk(KERN_DEBUG"isar selftest OK\n"); + } else { + printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n", + ireg->cmsb, ireg->clsb, ireg->par[0]); + ret = 1;goto reterror; + } + ireg->iis = 0; + if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) { + printk(KERN_ERR"isar RQST SVN failed\n"); + ret = 1;goto reterror; + } + cnt = 10000; /* max 100 ms */ + while ((ireg->iis != ISAR_IIS_DIAG) && cnt) { + udelay(10); + cnt--; + } + if (!cnt) { + printk(KERN_ERR"isar no SVN response\n"); + ret = 1;goto reterrflg; + } else { + if ((ireg->cmsb == ISAR_CTRL_SWVER) && (ireg->clsb == 1)) + printk(KERN_DEBUG"isar software version %#x\n", + ireg->par[0]); + else { + printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n", + ireg->cmsb, ireg->clsb, cnt); + ret = 1;goto reterrflg; + } + } + cs->debug = debug; + isar_setup(cs); + ret = 0; +reterrflg: + restore_flags(flags); +reterror: + cs->debug = debug; + if (ret) + /* disable ISAR IRQ */ + cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0); + kfree(msg); + kfree(tmpmsg); + return(ret); +} + +void +isar_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static inline void +isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs) +{ + u_char *ptr; + struct sk_buff *skb; + struct isar_reg *ireg = bcs->hw.isar.reg; + + if (!ireg->clsb) { + debugl1(cs, "isar zero len frame"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + return; + } + switch (bcs->mode) { + case L1_MODE_NULL: + debugl1(cs, "isar mode 0 spurious IIS_RDATA %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar mode 0 spurious IIS_RDATA %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + break; + case L1_MODE_TRANS: + if ((skb = dev_alloc_skb(ireg->clsb))) { + SET_SKB_FREE(skb); + rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb)); + skb_queue_tail(&bcs->rqueue, skb); + isar_sched_event(bcs, B_RCVBUFREADY); + } else { + printk(KERN_WARNING "HiSax: skb out of memory\n"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case L1_MODE_HDLC: + if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar_rcv_frame: incoming packet too large"); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + bcs->hw.isar.rcvidx = 0; + } else if (ireg->cmsb & HDLC_ERROR) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "isar frame error %x len %d", + ireg->cmsb, ireg->clsb); + bcs->hw.isar.rcvidx = 0; + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } else { + if (ireg->cmsb & HDLC_FSD) + bcs->hw.isar.rcvidx = 0; + ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx; + bcs->hw.isar.rcvidx += ireg->clsb; + rcv_mbox(cs, ireg, ptr); + if (ireg->cmsb & HDLC_FED) { + if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */ + printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n", + bcs->hw.isar.rcvidx); + } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2))) + printk(KERN_WARNING "ISAR: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2), + bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2); + skb_queue_tail(&bcs->rqueue, skb); + isar_sched_event(bcs, B_RCVBUFREADY); + } + } + } + break; + default: + printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + break; + } +} + +void +isar_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int count; + u_char msb; + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "isar_fill_fifo"); + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + if (!(bcs->hw.isar.reg->bstat & + (bcs->hw.isar.dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2))) + return; + if (bcs->tx_skb->len > bcs->hw.isar.mml) { + msb = 0; + count = bcs->hw.isar.mml; + } else { + count = bcs->tx_skb->len; + msb = HDLC_FED; + } + if (!bcs->hw.isar.txcnt) + msb |= HDLC_FST; + save_flags(flags); + cli(); + ptr = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.isar.txcnt += count; + switch (bcs->mode) { + case L1_MODE_NULL: + printk(KERN_ERR"isar_fill_fifo wrong mode 0\n"); + break; + case L1_MODE_TRANS: + if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + 0, count, ptr)) { + if (cs->debug) + debugl1(cs, "isar bin data send dp%d failed", + bcs->hw.isar.dpath); + } + break; + case L1_MODE_HDLC: + if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA, + msb, count, ptr)) { + if (cs->debug) + debugl1(cs, "isar hdlc data send dp%d failed", + bcs->hw.isar.dpath); + } + break; + default: + printk(KERN_ERR"isar_fill_fifo mode (%x)error\n", bcs->mode); + break; + } + restore_flags(flags); +} + +inline +struct BCState *sel_bcs_isar(struct IsdnCardState *cs, u_char dpath) +{ + if ((!dpath) || (dpath == 3)) + return(NULL); + if (cs->bcs[0].hw.isar.dpath == dpath) + return(&cs->bcs[0]); + if (cs->bcs[1].hw.isar.dpath == dpath) + return(&cs->bcs[1]); + return(NULL); +} + +inline void +send_frames(struct BCState *bcs) +{ + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + isar_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt); + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->hw.isar.txcnt = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.isar.txcnt = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + isar_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + isar_sched_event(bcs, B_XMTBUFREADY); + } +} + +inline void +check_send(struct IsdnCardState *cs, u_char rdm) +{ + struct BCState *bcs; + + if (rdm & BSTAT_RDM1) { + if ((bcs = sel_bcs_isar(cs, 1))) { + if (bcs->mode) { + send_frames(bcs); + } + } + } + if (rdm & BSTAT_RDM2) { + if ((bcs = sel_bcs_isar(cs, 2))) { + if (bcs->mode) { + send_frames(bcs); + } + } + } + +} + +static char debbuf[64]; + +void +isar_int_main(struct IsdnCardState *cs) +{ + long flags; + struct isar_reg *ireg = cs->bcs[0].hw.isar.reg; + struct BCState *bcs; + + save_flags(flags); + cli(); + get_irq_infos(cs, ireg); + switch (ireg->iis & ISAR_IIS_MSCMSD) { + case ISAR_IIS_RDATA: + if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) { + isar_rcv_frame(cs, bcs); + } else { + debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x", + ireg->iis, ireg->cmsb, ireg->clsb); + printk(KERN_WARNING"isar spurious IIS_RDATA %x/%x/%x\n", + ireg->iis, ireg->cmsb, ireg->clsb); + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + } + break; + case ISAR_IIS_GSTEV: + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + ireg->bstat |= ireg->cmsb; + check_send(cs, ireg->cmsb); + break; + case ISAR_IIS_BSTEV: + cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "Buffer STEV dpath%d msb(%x)", + ireg->iis>>6, ireg->cmsb); + break; + case ISAR_IIS_DIAG: + case ISAR_IIS_PSTRSP: + case ISAR_IIS_PSTEV: + case ISAR_IIS_BSTRSP: + case ISAR_IIS_IOM2RSP: + rcv_mbox(cs, ireg, (u_char *)ireg->par); + if ((cs->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO)) + == L1_DEB_HSCX) { + u_char *tp=debbuf; + + tp += sprintf(debbuf, "msg iis(%x) msb(%x)", + ireg->iis, ireg->cmsb); + QuickHex(tp, (u_char *)ireg->par, ireg->clsb); + debugl1(cs, debbuf); + } + break; + default: + rcv_mbox(cs, ireg, debbuf); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "unhandled msg iis(%x) ctrl(%x/%x)", + ireg->iis, ireg->cmsb, ireg->clsb); + break; + } + restore_flags(flags); +} + +void +setup_pump(struct BCState *bcs) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + + switch (bcs->mode) { + case L1_MODE_NULL: + case L1_MODE_TRANS: + case L1_MODE_HDLC: + if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL)) { + if (cs->debug) + debugl1(cs, "isar pump bypass cfg dp%d failed", + bcs->hw.isar.dpath); + } + break; + } + if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) { + if (cs->debug) + debugl1(cs, "isar pump status req dp%d failed", + bcs->hw.isar.dpath); + } +} + +void +setup_sart(struct BCState *bcs) { + struct IsdnCardState *cs = bcs->cs; + u_char dps = SET_DPS(bcs->hw.isar.dpath); + + switch (bcs->mode) { + case L1_MODE_NULL: + if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL)) { + if (cs->debug) + debugl1(cs, "isar sart disable dp%d failed", + bcs->hw.isar.dpath); + } + break; + case L1_MODE_TRANS: + if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0")) { + if (cs->debug) + debugl1(cs, "isar sart binary dp%d failed", + bcs->hw.isar.dpath); + } + break; + 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", + bcs->hw.isar.dpath); + } + break; + } + if (!sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL)) { + if (cs->debug) + debugl1(cs, "isar buf stat req dp%d failed", + bcs->hw.isar.dpath); + } +} + +void +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}; + + switch (bcs->mode) { + case L1_MODE_NULL: + /* 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; + } + if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) { + if (cs->debug) + debugl1(cs, "isar iom2 dp%d failed", bcs->hw.isar.dpath); + } + if (!sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL)) { + if (cs->debug) + debugl1(cs, "isar IOM2 cfg req dp%d failed", + bcs->hw.isar.dpath); + } +} + +int +modeisar(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + /* Here we are selecting the best datapath for requested mode */ + if(bcs->mode == L1_MODE_NULL) { /* New Setup */ + bcs->channel = bc; + switch (mode) { + case L1_MODE_NULL: /* init */ + break; + case L1_MODE_TRANS: + case L1_MODE_HDLC: + /* best is datapath 2 */ + if (!test_and_set_bit(ISAR_DP2_USE, + &bcs->hw.isar.reg->Flags)) + bcs->hw.isar.dpath = 2; + else if (!test_and_set_bit(ISAR_DP1_USE, + &bcs->hw.isar.reg->Flags)) + bcs->hw.isar.dpath = 1; + else { + printk(KERN_ERR"isar modeisar both pathes in use\n"); + return(1); + } + break; + } + } + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "isar dp%d mode %d->%d ichan %d", + bcs->hw.isar.dpath, bcs->mode, mode, bc); + bcs->mode = mode; + setup_pump(bcs); + setup_sart(bcs); + setup_iom2(bcs); + if (bcs->mode == L1_MODE_NULL) { + /* Clear resources */ + if (bcs->hw.isar.dpath == 1) + test_and_clear_bit(ISAR_DP1_USE, &bcs->hw.isar.reg->Flags); + else if (bcs->hw.isar.dpath == 2) + test_and_clear_bit(ISAR_DP2_USE, &bcs->hw.isar.reg->Flags); + bcs->hw.isar.dpath = 0; + } + return(0); +} + +void +isar_setup(struct IsdnCardState *cs) +{ + u_char msg; + int i; + + /* Dpath 1, 2 */ + msg = 61; + for (i=0; i<2; i++) { + /* Buffer Config */ + if (!sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) | + ISAR_HIS_P12CFG, 4, 1, &msg)) { + if (cs->debug) + debugl1(cs, "isar P%dCFG failed", i+1); + } + cs->bcs[i].hw.isar.mml = msg; + cs->bcs[i].mode = 0; + cs->bcs[i].hw.isar.dpath = i + 1; + modeisar(&cs->bcs[i], 0, 0); + } +} + +void +isar_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + if (st->l1.bcs->cs->debug & L1_DEB_HSCX) + debugl1(st->l1.bcs->cs, "DRQ set BC_FLG_BUSY"); + st->l1.bcs->hw.isar.txcnt = 0; + restore_flags(flags); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "isar_l2l1: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + if (st->l1.bcs->cs->debug & L1_DEB_HSCX) + debugl1(st->l1.bcs->cs, "PUI set BC_FLG_BUSY"); + st->l1.bcs->tx_skb = skb; + st->l1.bcs->hw.isar.txcnt = 0; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + 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); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + if (st->l1.bcs->cs->debug & L1_DEB_HSCX) + debugl1(st->l1.bcs->cs, "PDAC clear BC_FLG_BUSY"); + modeisar(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +void +close_isarstate(struct BCState *bcs) +{ + modeisar(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.isar.rcvbuf) { + kfree(bcs->hw.isar.rcvbuf); + bcs->hw.isar.rcvbuf = NULL; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY"); + } + } +} + +int +open_isarstate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.isar.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for isar.rcvbuf\n"); + return (1); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "openisar clear BC_FLG_BUSY"); + bcs->event = 0; + bcs->hw.isar.rcvidx = 0; + bcs->tx_cnt = 0; + return (0); +} + +int +setstack_isar(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_isarstate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = isar_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +HISAX_INITFUNC(void +initisar(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_isar; + cs->bcs[1].BC_SetStack = setstack_isar; + cs->bcs[0].BC_Close = close_isarstate; + cs->bcs[1].BC_Close = close_isarstate; +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isar.h linux/drivers/isdn/hisax/isar.h --- v2.0.35/linux/drivers/isdn/hisax/isar.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/isar.h Sun Nov 15 10:32:58 1998 @@ -0,0 +1,97 @@ +/* $Id: isar.h,v 1.1.2.3 1998/10/04 23:05:01 keil Exp $ + * isar.h ISAR (Siemens PSB 7110) specific defines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: isar.h,v $ + * Revision 1.1.2.3 1998/10/04 23:05:01 keil + * ISAR works now + * + * Revision 1.1.2.2 1998/09/30 22:28:09 keil + * more work for isar support + * + * Revision 1.1.2.1 1998/09/27 13:01:44 keil + * Start support for ISAR based cards + * + * Revision 1.1 1998/08/13 23:33:48 keil + * First version, only init + * + * + */ + +#define ISAR_IRQMSK 0x04 +#define ISAR_IRQSTA 0x04 +#define ISAR_IRQBIT 0x75 +#define ISAR_CTRL_H 0x61 +#define ISAR_CTRL_L 0x60 +#define ISAR_IIS 0x58 +#define ISAR_IIA 0x58 +#define ISAR_HIS 0x50 +#define ISAR_HIA 0x50 +#define ISAR_MBOX 0x4c +#define ISAR_WADR 0x4a +#define ISAR_RADR 0x48 + +#define ISAR_HIS_VNR 0x14 +#define ISAR_HIS_DKEY 0x02 +#define ISAR_HIS_FIRM 0x1e +#define ISAR_HIS_STDSP 0x08 +#define ISAR_HIS_DIAG 0x05 +#define ISAR_HIS_P0CFG 0x3c +#define ISAR_HIS_P12CFG 0x24 +#define ISAR_HIS_SARTCFG 0x25 +#define ISAR_HIS_PUMPCFG 0x26 +#define ISAR_HIS_IOM2CFG 0x27 +#define ISAR_HIS_IOM2REQ 0x07 +#define ISAR_HIS_BSTREQ 0x0c +#define ISAR_HIS_PSTREQ 0x0e +#define ISAR_HIS_SDATA 0x20 +#define ISAR_HIS_DPS1 0x40 +#define ISAR_HIS_DPS2 0x80 +#define SET_DPS(x) ((x<<6) & 0xc0) + +#define ISAR_IIS_MSCMSD 0x3f +#define ISAR_IIS_VNR 0x15 +#define ISAR_IIS_DKEY 0x03 +#define ISAR_IIS_FIRM 0x1f +#define ISAR_IIS_STDSP 0x09 +#define ISAR_IIS_DIAG 0x25 +#define ISAR_IIS_GSTEV 0x0 +#define ISAR_IIS_BSTEV 0x28 +#define ISAR_IIS_BSTRSP 0x2c +#define ISAR_IIS_PSTRSP 0x2e +#define ISAR_IIS_PSTEV 0x2a +#define ISAR_IIS_IOM2RSP 0x27 + +#define ISAR_IIS_RDATA 0x20 +#define ISAR_CTRL_SWVER 0x10 +#define ISAR_CTRL_STST 0x40 + +#define ISAR_MSG_HWVER {0x20, 0, 1} + +#define ISAR_DP1_USE 1 +#define ISAR_DP2_USE 2 + +#define PMOD_BYPASS 7 + +#define SMODE_DISABLE 0 +#define SMODE_HDLC 3 +#define SMODE_BINARY 4 + +#define HDLC_FED 0x40 +#define HDLC_FSD 0x20 +#define HDLC_FST 0x20 +#define HDLC_ERROR 0x1c + +#define BSTAT_RDM0 0x1 +#define BSTAT_RDM1 0x2 +#define BSTAT_RDM2 0x4 +#define BSTAT_RDM3 0x8 + + +extern int ISARVersion(struct IsdnCardState *cs, char *s); +extern int isar_load_firmware(struct IsdnCardState *cs, u_char *buf); +extern void isar_int_main(struct IsdnCardState *cs); +extern void initisar(struct IsdnCardState *cs); +extern void isar_fill_fifo(struct BCState *bcs); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c --- v2.0.35/linux/drivers/isdn/hisax/isdnl1.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/isdnl1.c Sun Nov 15 10:32:58 1998 @@ -1,9 +1,13 @@ -/* $Id: isdnl1.c,v 1.15 1997/05/27 15:17:55 fritz Exp $ +/* $Id: isdnl1.c,v 1.15.2.19 1998/11/03 00:06:48 keil Exp $ * isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards * based on the teles driver from Jan den Ouden * - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert * * Thanks to Jan den Ouden * Fritz Elfert @@ -11,630 +15,379 @@ * * * $Log: isdnl1.c,v $ - * Revision 1.15 1997/05/27 15:17:55 fritz - * Added changes for recent 2.1.x kernels: - * changed return type of isdn_close - * queue_task_* -> queue_task - * clear/set_bit -> test_and_... where apropriate. - * changed type of hard_header_cache parameter. + * Revision 1.15.2.19 1998/11/03 00:06:48 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.15.2.18 1998/09/30 22:26:35 keil + * Add init of l1.Flags + * + * Revision 1.15.2.17 1998/09/27 23:54:17 keil + * cosmetics + * + * Revision 1.15.2.16 1998/09/27 13:06:22 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.15.2.15 1998/09/12 18:44:00 niemann + * Added new card: Sedlbauer ISDN-Controller PC/104 + * + * Revision 1.15.2.14 1998/08/25 14:01:35 calle + * Ported driver for AVM Fritz!Card PCI from the 2.1 tree. + * I could not test it. + * + * Revision 1.15.2.13 1998/07/15 14:43:37 calle + * Support for AVM passive PCMCIA cards: + * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0 + * + * Revision 1.15.2.12 1998/05/27 18:05:43 keil + * HiSax 3.0 + * + * Revision 1.15.2.11 1998/05/26 10:36:51 keil + * fixes from certification * - * Revision 1.14 1997/04/07 23:00:08 keil - * GFP_KERNEL ---> GFP_ATOMIC + * Revision 1.15.2.10 1998/04/11 18:47:45 keil + * Fixed bug which was overwriting nrcards + * New card support * - * Revision 1.13 1997/04/06 22:55:50 keil - * Using SKB's + * Revision 1.15.2.9 1998/04/08 21:52:00 keil + * new debug * - * Revision 1.12 1997/03/26 13:43:57 keil - * small cosmetics + * Revision 1.15.2.8 1998/03/07 23:15:26 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.11 1997/03/25 23:11:23 keil - * US NI-1 protocol + * Revision 1.15.2.7 1998/02/11 14:23:14 keil + * support for Dr Neuhaus Niccy PnP and PCI * - * Revision 1.10 1997/03/13 14:45:05 keil - * using IRQ proof queue_task + * Revision 1.15.2.6 1998/02/09 11:24:11 keil + * New leased line support (Read README.HiSax!) * - * Revision 1.9 1997/03/12 21:44:21 keil - * change Interrupt routine from atomic quick to normal + * Revision 1.15.2.5 1998/01/27 22:33:55 keil + * dynalink ----> asuscom * - * Revision 1.8 1997/02/09 00:24:31 keil - * new interface handling, one interface per card + * Revision 1.15.2.4 1998/01/11 22:55:20 keil + * 16.3c support * - * Revision 1.7 1997/01/27 15:56:03 keil - * PCMCIA Teles card and ITK ix1 micro added + * Revision 1.15.2.3 1997/11/15 18:50:34 keil + * new common init function * - * Revision 1.6 1997/01/21 22:20:00 keil - * changes for D-channel log; Elsa Quickstep support + * Revision 1.15.2.2 1997/10/17 22:13:54 keil + * update to last hisax version * - * Revision 1.5 1997/01/10 12:51:19 keil - * cleanup; set newversion + * Revision 2.6 1997/09/12 10:05:16 keil + * ISDN_CTRL_DEBUG define * - * Revision 1.4 1996/12/08 19:44:53 keil - * L2FRAME_DEBUG and other changes from Pekka Sarnila + * Revision 2.5 1997/09/11 17:24:45 keil + * Add new cards * - * Revision 1.3 1996/11/18 15:34:47 keil - * fix HSCX version code + * Revision 2.4 1997/08/15 17:47:09 keil + * avoid oops because a uninitialised timer * - * Revision 1.2 1996/10/27 22:16:54 keil - * ISAC/HSCX version lookup + * Revision 2.3 1997/08/01 11:16:40 keil + * cosmetics * - * Revision 1.1 1996/10/13 20:04:53 keil - * Initial revision + * Revision 2.2 1997/07/30 17:11:08 keil + * L1deactivated exported * + * Revision 2.1 1997/07/27 21:35:38 keil + * new layer1 interface * + * Revision 2.0 1997/06/26 11:02:53 keil + * New Layer and card interface + * + * Revision 1.15 1997/05/27 15:17:55 fritz + * Added changes for recent 2.1.x kernels: + * changed return type of isdn_close + * queue_task_* -> queue_task + * clear/set_bit -> test_and_... where apropriate. + * changed type of hard_header_cache parameter. + * + * old changes removed KKe * */ -const char *l1_revision = "$Revision: 1.15 $"; +const char *l1_revision = "$Revision: 1.15.2.19 $"; #define __NO_VERSION__ #include #include "hisax.h" #include "isdnl1.h" -#if CARD_TELES0 -#include "teles0.h" -#endif - -#if CARD_TELES3 -#include "teles3.h" -#endif - -#if CARD_AVM_A1 -#include "avm_a1.h" -#endif - -#if CARD_ELSA -#include "elsa.h" -#endif +#define TIMER3_VALUE 7000 -#if CARD_IX1MICROR2 -#include "ix1_micro.h" -#endif - -/* #define I4L_IRQ_FLAG SA_INTERRUPT */ -#define I4L_IRQ_FLAG 0 - -#define HISAX_STATUS_BUFSIZE 4096 - -#define INCLUDE_INLINE_FUNCS -#include -#include - -const char *CardType[] = -{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", - "Creatix/Teles PnP", "AVM A1", "Elsa ML", -#ifdef CONFIG_HISAX_ELSA_PCMCIA - "Elsa PCMCIA", -#else - "Elsa Quickstep", -#endif - "Teles PCMCIA", "ITK ix1-micro Rev.2"}; - -static char *HSCXVer[] = -{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7", - "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"}; - -static char *ISACVer[] = -{"2086/2186 V1.1", "2085 B1", "2085 B2", - "2085 V2.3"}; - -extern struct IsdnCard cards[]; -extern int nrcards; -extern char *HiSax_id; - -/* - * Find card with given driverId - */ -static inline struct IsdnCardState -* -hisax_findcard(int driverid) -{ - int i; - - for (i = 0; i < nrcards; i++) - if (cards[i].sp) - if (cards[i].sp->myid == driverid) - return (cards[i].sp); - return (struct IsdnCardState *) 0; -} - -int -HiSax_readstatus(u_char * buf, int len, int user, int id, int channel) -{ - int count; - u_char *p; - struct IsdnCardState *csta = hisax_findcard(id); - - if (csta) { - for (p = buf, count = 0; count < len; p++, count++) { - if (user) - put_user(*csta->status_read++, p); - else - *p++ = *csta->status_read++; - if (csta->status_read > csta->status_end) - csta->status_read = csta->status_buf; - } - return count; - } else { - printk(KERN_ERR - "HiSax: if_readstatus called with invalid driverId!\n"); - return -ENODEV; - } +static +struct Fsm l1fsm_b = +{NULL, 0, 0, NULL, NULL}; + +static +struct Fsm l1fsm_d = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1D_STATE_COUNT (ST_L1_F8+1) + +static char *strL1DState[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + ST_L1_NULL, + ST_L1_WAIT_ACT, + ST_L1_WAIT_DEACT, + ST_L1_ACTIV, +}; + +#define L1B_STATE_COUNT (ST_L1_ACTIV+1) + +static char *strL1BState[] = +{ + "ST_L1_NULL", + "ST_L1_WAIT_ACT", + "ST_L1_WAIT_DEACT", + "ST_L1_ACTIV", +}; + +enum { + EV_PH_ACTIVATE, + EV_PH_DEACTIVATE, + EV_RESET_IND, + EV_DEACT_CNF, + EV_DEACT_IND, + EV_POWER_UP, + EV_RSYNC_IND, + EV_INFO2_IND, + EV_INFO4_IND, + EV_TIMER_DEACT, + EV_TIMER_ACT, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_ACTIVATE", + "EV_PH_DEACTIVATE", + "EV_RESET_IND", + "EV_DEACT_CNF", + "EV_DEACT_IND", + "EV_POWER_UP", + "EV_RSYNC_IND", + "EV_INFO2_IND", + "EV_INFO4_IND", + "EV_TIMER_DEACT", + "EV_TIMER_ACT", + "EV_TIMER3", +}; + +void +debugl1(struct IsdnCardState *cs, char *fmt, ...) +{ + va_list args; + char tmp[8]; + + va_start(args, fmt); + sprintf(tmp, "Card%d ", cs->cardnr + 1); + VHiSax_putstatus(cs, tmp, fmt, args); + va_end(args); +} + +static void +l1m_debug(struct FsmInst *fi, char *fmt, ...) +{ + va_list args; + struct PStack *st = fi->userdata; + struct IsdnCardState *cs = st->l1.hardware; + char tmp[8]; + + va_start(args, fmt); + sprintf(tmp, "Card%d ", cs->cardnr + 1); + VHiSax_putstatus(cs, tmp, fmt, args); + va_end(args); } void -HiSax_putstatus(struct IsdnCardState *csta, char *buf) -{ - long flags; - int len, count, i; - u_char *p; - isdn_ctrl ic; - - save_flags(flags); - cli(); - count = 0; - len = strlen(buf); - - if (!csta) { - printk(KERN_WARNING "HiSax: No CardStatus for message %s", buf); - restore_flags(flags); - return; - } - for (p = buf, i = len; i > 0; i--, p++) { - *csta->status_write++ = *p; - if (csta->status_write > csta->status_end) - csta->status_write = csta->status_buf; - count++; - } - restore_flags(flags); - if (count) { - ic.command = ISDN_STAT_STAVAIL; - ic.driver = csta->myid; - ic.arg = count; - csta->iif.statcallb(&ic); - } -} - -int -ll_run(struct IsdnCardState *csta) -{ - long flags; - isdn_ctrl ic; - - save_flags(flags); - cli(); - ic.driver = csta->myid; - ic.command = ISDN_STAT_RUN; - csta->iif.statcallb(&ic); - restore_flags(flags); - return 0; -} - -void -ll_stop(struct IsdnCardState *csta) -{ - isdn_ctrl ic; - - ic.command = ISDN_STAT_STOP; - ic.driver = csta->myid; - csta->iif.statcallb(&ic); - CallcFreeChan(csta); -} - -static void -ll_unload(struct IsdnCardState *csta) -{ - isdn_ctrl ic; - - ic.command = ISDN_STAT_UNLOAD; - ic.driver = csta->myid; - csta->iif.statcallb(&ic); - if (csta->status_buf) - kfree(csta->status_buf); - csta->status_read = NULL; - csta->status_write = NULL; - csta->status_end = NULL; - kfree(csta->dlogspace); -} - -void -debugl1(struct IsdnCardState *sp, char *msg) -{ - char tmp[256], tm[32]; - - jiftime(tm, jiffies); - sprintf(tmp, "%s Card %d %s\n", tm, sp->cardnr + 1, msg); - HiSax_putstatus(sp, tmp); -} - -/* - * HSCX stuff goes here - */ - - -char * -HscxVersion(u_char v) -{ - return (HSCXVer[v & 0xf]); -} - -void -hscx_sched_event(struct HscxState *hsp, int event) -{ - hsp->event |= 1 << event; - queue_task(&hsp->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -/* - * ISAC stuff goes here - */ - -char * -ISACVersion(u_char v) -{ - return (ISACVer[(v >> 5) & 3]); -} - -void -isac_sched_event(struct IsdnCardState *sp, int event) -{ - sp->event |= 1 << event; - queue_task(&sp->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -int -act_wanted(struct IsdnCardState *sp) +L1activated(struct IsdnCardState *cs) { struct PStack *st; - st = sp->stlist; - while (st) - if (st->l1.act_state) - return (!0); + st = cs->stlist; + while (st) { + if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); else - st = st->next; - return (0); -} - -void -isac_new_ph(struct IsdnCardState *sp) -{ - int enq; - - enq = act_wanted(sp); - - switch (sp->ph_state) { - case (6): - sp->ph_active = 0; - sp->ph_command(sp, 15); - break; - case (15): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 0); - break; - case (0): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 0); -#if 0 - else - sp->ph_command(sp, 15); -#endif - break; - case (7): - sp->ph_active = 0; - if (enq) - sp->ph_command(sp, 9); - break; - case (12): - sp->ph_command(sp, 8); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->tx_skb) - sp->tx_skb = skb_dequeue(&sp->sq); - if (sp->tx_skb) { - sp->tx_cnt = 0; - sp->isac_fill_fifo(sp); - } - break; - case (13): - sp->ph_command(sp, 9); - sp->ph_active = 5; - isac_sched_event(sp, ISAC_PHCHANGE); - if (!sp->tx_skb) - sp->tx_skb = skb_dequeue(&sp->sq); - if (sp->tx_skb) { - sp->tx_cnt = 0; - sp->isac_fill_fifo(sp); - } - break; - case (4): - case (8): - sp->ph_active = 0; - break; - default: - sp->ph_active = 0; - break; - } -} - -static void -restart_ph(struct IsdnCardState *sp) -{ - if (!sp->ph_active) { - if ((sp->ph_state == 6) || (sp->ph_state == 0)) { - sp->ph_command(sp, 0); - sp->ph_active = 2; - } else { - sp->ph_command(sp, 1); - sp->ph_active = 1; - } - } else if (sp->ph_active == 2) { - sp->ph_command(sp, 1); - sp->ph_active = 1; + st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL); + st = st->next; } } - -static void -act_ivated(struct IsdnCardState *sp) +void +L1deactivated(struct IsdnCardState *cs) { struct PStack *st; - st = sp->stlist; + st = cs->stlist; while (st) { - if (st->l1.act_state == 1) { - st->l1.act_state = 2; - st->l1.l1man(st, PH_ACTIVATE, NULL); - } + if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL); + st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL); st = st->next; } + test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); } -static void -process_new_ph(struct IsdnCardState *sp) -{ - if (sp->ph_active == 5) - act_ivated(sp); -} - -static void -process_xmt(struct IsdnCardState *sp) +void +DChannel_proc_xmt(struct IsdnCardState *cs) { struct PStack *stptr; - if (sp->tx_skb) + if (cs->tx_skb) return; - stptr = sp->stlist; + stptr = cs->stlist; while (stptr != NULL) - if (stptr->l1.requestpull) { - stptr->l1.requestpull = 0; - stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) { + stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL); break; } else stptr = stptr->next; } -static void -process_rcv(struct IsdnCardState *sp) +void +DChannel_proc_rcv(struct IsdnCardState *cs) { struct sk_buff *skb, *nskb; - struct PStack *stptr; - int found, broadc; - char tmp[64]; + struct PStack *stptr = cs->stlist; + int found, tei, sapi; - while ((skb = skb_dequeue(&sp->rq))) { + if (stptr) + if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags)) + FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL); + while ((skb = skb_dequeue(&cs->rq))) { #ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA", 1); + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 1); #endif - stptr = sp->stlist; - broadc = (skb->data[1] >> 1) == 127; - - if (broadc) { - if (!(skb->data[0] >> 2)) { /* sapi 0 */ - sp->CallFlags = 3; - if (sp->dlogflag) { - LogFrame(sp, skb->data, skb->len); - dlogframe(sp, skb->data + 3, skb->len - 3, - "Q.931 frame network->user broadcast"); - } - } - while (stptr != NULL) { - if ((skb->data[0] >> 2) == stptr->l2.sap) + stptr = cs->stlist; + sapi = skb->data[0] >> 2; + tei = skb->data[1] >> 1; + + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 1); + if (tei == GROUP_TEI) { + if (sapi == CTRL_SAPI) { /* sapi 0 */ + while (stptr != NULL) { if ((nskb = skb_clone(skb, GFP_ATOMIC))) - stptr->l1.l1l2(stptr, PH_DATA, nskb); + stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb); else printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n"); - stptr = stptr->next; + stptr = stptr->next; + } + } else if (sapi == TEI_SAPI) { + while (stptr != NULL) { + if ((nskb = skb_clone(skb, GFP_ATOMIC))) + stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb); + else + printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n"); + stptr = stptr->next; + } } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - } else { + } else if (sapi == CTRL_SAPI) { /* sapi 0 */ found = 0; while (stptr != NULL) - if (((skb->data[0] >> 2) == stptr->l2.sap) && - ((skb->data[1] >> 1) == stptr->l2.tei)) { - stptr->l1.l1l2(stptr, PH_DATA, skb); + if (tei == stptr->l2.tei) { + stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb); found = !0; break; } else stptr = stptr->next; - if (!found) { - /* BD 10.10.95 - * Print out D-Channel msg not processed - * by isdn4linux - */ - - if ((!(skb->data[0] >> 2)) && (!(skb->data[2] & 0x01))) { - sprintf(tmp, - "Q.931 frame network->user with tei %d (not for us)", - skb->data[1] >> 1); - LogFrame(sp, skb->data, skb->len); - dlogframe(sp, skb->data + 4, skb->len - 4, tmp); - } - SET_SKB_FREE(skb); + if (!found) dev_kfree_skb(skb, FREE_READ); - } } - - } - -} - -static void -isac_bh(struct IsdnCardState *sp) -{ - if (!sp) - return; - - if (test_and_clear_bit(ISAC_PHCHANGE, &sp->event)) - process_new_ph(sp); - if (test_and_clear_bit(ISAC_RCVBUFREADY, &sp->event)) - process_rcv(sp); - if (test_and_clear_bit(ISAC_XMTBUFREADY, &sp->event)) - process_xmt(sp); -} - -static void -l2l1(struct PStack *st, int pr, void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct sk_buff *skb = arg; - char str[64]; - - switch (pr) { - case (PH_DATA): - if (sp->tx_skb) { - skb_queue_tail(&sp->sq, skb); -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA Queued", 0); -#endif - } else { - if ((sp->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ - LogFrame(sp, skb->data, skb->len); - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize, - str); - } - sp->tx_skb = skb; - sp->tx_cnt = 0; -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA", 0); -#endif - sp->isac_fill_fifo(sp); - } - break; - case (PH_DATA_PULLED): - if (sp->tx_skb) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, " l2l1 tx_skb exist this shouldn't happen"); - break; - } - if ((sp->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */ - LogFrame(sp, skb->data, skb->len); - sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei); - dlogframe(sp, skb->data + st->l2.ihsize, skb->len - st->l2.ihsize, - str); - } - sp->tx_skb = skb; - sp->tx_cnt = 0; -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - Logl2Frame(sp, skb, "PH_DATA_PULLED", 0); -#endif - sp->isac_fill_fifo(sp); - break; - case (PH_REQUEST_PULL): -#ifdef L2FRAME_DEBUG /* psa */ - if (sp->debug & L1_DEB_LAPD) - debugl1(sp, "-> PH_REQUEST_PULL"); -#endif - if (!sp->tx_skb) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; } } - static void -hscx_process_xmt(struct HscxState *hsp) +BChannel_proc_xmt(struct BCState *bcs) { - struct PStack *st = hsp->st; + struct PStack *st = bcs->st; - if (hsp->tx_skb) + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) { + debugl1(bcs->cs, "BC_BUSY Error"); return; + } - if (st->l1.requestpull) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); + if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) { + if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) { + st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL); + } } - if (!hsp->active) - if ((!hsp->tx_skb) && (!skb_queue_len(&hsp->squeue))) - hsp->sp->modehscx(hsp, 0, 0); } static void -hscx_process_rcv(struct HscxState *hsp) +BChannel_proc_rcv(struct BCState *bcs) { struct sk_buff *skb; -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n"); - return; + if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) { + FsmDelTimer(&bcs->st->l1.timer, 4); + FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL); } -#endif - while ((skb = skb_dequeue(&hsp->rqueue))) { - hsp->st->l1.l1l2(hsp->st, PH_DATA, skb); + while ((skb = skb_dequeue(&bcs->rqueue))) { + bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb); } } static void -hscx_bh(struct HscxState *hsp) +BChannel_bh(struct BCState *bcs) { - - if (!hsp) + if (!bcs) return; - - if (test_and_clear_bit(HSCX_RCVBUFREADY, &hsp->event)) - hscx_process_rcv(hsp); - if (test_and_clear_bit(HSCX_XMTBUFREADY, &hsp->event)) - hscx_process_xmt(hsp); - + if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event)) + BChannel_proc_rcv(bcs); + if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event)) + BChannel_proc_xmt(bcs); } -/* - * interrupt stuff ends here - */ - void -HiSax_addlist(struct IsdnCardState *sp, +HiSax_addlist(struct IsdnCardState *cs, struct PStack *st) { - st->next = sp->stlist; - sp->stlist = st; + st->next = cs->stlist; + cs->stlist = st; } void -HiSax_rmlist(struct IsdnCardState *sp, +HiSax_rmlist(struct IsdnCardState *cs, struct PStack *st) { struct PStack *p; - if (sp->stlist == st) - sp->stlist = st->next; + FsmDelTimer(&st->l1.timer, 0); + if (cs->stlist == st) + cs->stlist = st->next; else { - p = sp->stlist; + p = cs->stlist; while (p) if (p->next == st) { p->next = st->next; @@ -644,735 +397,477 @@ } } -static void -check_ph_act(struct IsdnCardState *sp) +void +init_bcstate(struct IsdnCardState *cs, + int bc) { - struct PStack *st = sp->stlist; + struct BCState *bcs = cs->bcs + bc; - while (st) { - if (st->l1.act_state) - return; - st = st->next; - } - if (sp->ph_active == 5) - sp->ph_active = 4; + bcs->cs = cs; + bcs->channel = bc; + bcs->tqueue.next = 0; + bcs->tqueue.sync = 0; + bcs->tqueue.routine = (void *) (void *) BChannel_bh; + bcs->tqueue.data = bcs; + bcs->BC_SetStack = NULL; + bcs->BC_Close = NULL; + bcs->Flag = 0; } -static void -HiSax_manl1(struct PStack *st, int pr, - void *arg) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - long flags; - char tmp[32]; +#ifdef L2FRAME_DEBUG /* psa */ - switch (pr) { - case (PH_ACTIVATE): - if (sp->debug) { - sprintf(tmp, "PH_ACT ph_active %d", sp->ph_active); - debugl1(sp, tmp); - } - save_flags(flags); - cli(); - if (sp->ph_active & 4) { - sp->ph_active = 5; - st->l1.act_state = 2; - restore_flags(flags); - st->l1.l1man(st, PH_ACTIVATE, NULL); - } else { - st->l1.act_state = 1; - if (sp->ph_active == 0) - restart_ph(sp); - restore_flags(flags); - } - break; - case (PH_DEACTIVATE): - st->l1.act_state = 0; - if (sp->debug) { - sprintf(tmp, "PH_DEACT ph_active %d", sp->ph_active); - debugl1(sp, tmp); - } - check_ph_act(sp); - break; +char * +l2cmd(u_char cmd) +{ + switch (cmd & ~0x10) { + case 1: + return "RR"; + case 5: + return "RNR"; + case 9: + return "REJ"; + case 0x6f: + return "SABME"; + case 0x0f: + return "DM"; + case 3: + return "UI"; + case 0x43: + return "DISC"; + case 0x63: + return "UA"; + case 0x87: + return "FRMR"; + case 0xaf: + return "XID"; + default: + if (!(cmd & 1)) + return "I"; + else + return "invalid command"; } } -static void -HiSax_l2l1discardq(struct PStack *st, int pr, - void *heldby, int releasetoo) -{ - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct sk_buff *skb; +static char tmpdeb[32]; -#ifdef DEBUG_MAGIC - if (sp->magic != 301271) { - printk(KERN_DEBUG "isac_discardq magic not 301271\n"); - return; +char * +l2frames(u_char * ptr) +{ + switch (ptr[2] & ~0x10) { + case 1: + case 5: + case 9: + sprintf(tmpdeb, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1); + break; + case 0x6f: + case 0x0f: + case 3: + case 0x43: + case 0x63: + case 0x87: + case 0xaf: + sprintf(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4); + break; + default: + if (!(ptr[2] & 1)) { + sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1); + break; + } else + return "invalid command"; } -#endif - while ((skb = skb_dequeue(&sp->sq))) - dev_kfree_skb(skb, FREE_WRITE); + + return tmpdeb; } void -setstack_HiSax(struct PStack *st, struct IsdnCardState *sp) +Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir) { - st->l1.hardware = sp; - st->protocol = sp->protocol; + u_char *ptr; - setstack_tei(st); + ptr = skb->data; - st->l1.stlistp = &(sp->stlist); - st->l1.act_state = 0; - st->l2.l2l1 = l2l1; - st->l2.l2l1discardq = HiSax_l2l1discardq; - st->ma.manl1 = HiSax_manl1; - st->l1.requestpull = 0; + if (ptr[0] & 1 || !(ptr[1] & 1)) + debugl1(cs, "Address not LAPD"); + else + debugl1(cs, "%s %s: %s%c (sapi %d, tei %d)", + (dir ? "<-" : "->"), buf, l2frames(ptr), + ((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1); } +#endif -void -init_hscxstate(struct IsdnCardState *sp, - int hscx) +static void +l1_reset(struct FsmInst *fi, int event, void *arg) { - struct HscxState *hsp = sp->hs + hscx; - - hsp->sp = sp; - hsp->hscx = hscx; - - hsp->tqueue.next = 0; - hsp->tqueue.sync = 0; - hsp->tqueue.routine = (void *) (void *) hscx_bh; - hsp->tqueue.data = hsp; + FsmChangeState(fi, ST_L1_F3); +} - hsp->inuse = 0; - hsp->init = 0; - hsp->active = 0; +static void +l1_deact_cnf(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; -#ifdef DEBUG_MAGIC - hsp->magic = 301270; -#endif + FsmChangeState(fi, ST_L1_F3); + if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL); } -int -get_irq(int cardnr, void *routine) +static void +l1_deact_req(struct FsmInst *fi, int event, void *arg) { - struct IsdnCard *card = cards + cardnr; - long flags; + struct PStack *st = fi->userdata; - save_flags(flags); - cli(); - if (request_irq(card->sp->irq, routine, - I4L_IRQ_FLAG, "HiSax", NULL)) { - printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n", - card->sp->irq); - restore_flags(flags); - return (0); - } - irq2dev_map[card->sp->irq] = (void *) card->sp; - restore_flags(flags); - return (1); + FsmChangeState(fi, ST_L1_F3); +// if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) { + FsmDelTimer(&st->l1.timer, 1); + FsmAddTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); +// } } static void -release_irq(int cardnr) +l1_power_up(struct FsmInst *fi, int event, void *arg) { - struct IsdnCard *card = cards + cardnr; + struct PStack *st = fi->userdata; - irq2dev_map[card->sp->irq] = NULL; - free_irq(card->sp->irq, NULL); + if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) { + FsmChangeState(fi, ST_L1_F4); + st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); + FsmDelTimer(&st->l1.timer, 1); + FsmAddTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags); + } else + FsmChangeState(fi, ST_L1_F3); } -void -close_hscxstate(struct HscxState *hs) +static void +l1_go_F5(struct FsmInst *fi, int event, void *arg) { - struct sk_buff *skb; - - hs->sp->modehscx(hs, 0, 0); - hs->inuse = 0; - if (hs->init) { - if (hs->rcvbuf) { - kfree(hs->rcvbuf); - hs->rcvbuf = NULL; - } - while ((skb = skb_dequeue(&hs->rqueue))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - } - while ((skb = skb_dequeue(&hs->squeue))) - dev_kfree_skb(skb, FREE_WRITE); - if (hs->tx_skb) { - dev_kfree_skb(hs->tx_skb, FREE_WRITE); - hs->tx_skb = NULL; - } - } - hs->init = 0; + FsmChangeState(fi, ST_L1_F5); } static void -closecard(int cardnr) +l1_go_F8(struct FsmInst *fi, int event, void *arg) { - struct IsdnCardState *csta = cards[cardnr].sp; - struct sk_buff *skb; + FsmChangeState(fi, ST_L1_F8); +} - close_hscxstate(csta->hs + 1); - close_hscxstate(csta->hs); +static void +l1_info2_ind(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; - if (csta->rcvbuf) { - kfree(csta->rcvbuf); - csta->rcvbuf = NULL; - } - while ((skb = skb_dequeue(&csta->rq))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - } - while ((skb = skb_dequeue(&csta->sq))) - dev_kfree_skb(skb, FREE_WRITE); - if (csta->tx_skb) { - dev_kfree_skb(csta->tx_skb, FREE_WRITE); - csta->tx_skb = NULL; - } - switch (csta->typ) { -#if CARD_TELES0 - case ISDN_CTYPE_16_0: - case ISDN_CTYPE_8_0: - release_io_teles0(cards + cardnr); - break; -#endif -#if CARD_TELES3 - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_TELESPCMCIA: - release_io_teles3(cards + cardnr); - break; -#endif -#if CARD_AVM_A1 - case ISDN_CTYPE_A1: - release_io_avm_a1(cards + cardnr); - break; -#endif -#if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: - release_io_elsa(cards + cardnr); - break; -#endif -#if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - release_io_ix1micro(cards + cardnr); - break; -#endif - default: - break; - } - ll_unload(csta); + FsmChangeState(fi, ST_L1_F6); + st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); } -static int -checkcard(int cardnr, char *id) +static void +l1_info4_ind(struct FsmInst *fi, int event, void *arg) { - long flags; - int ret = 0; - struct IsdnCard *card = cards + cardnr; - struct IsdnCardState *sp; - - save_flags(flags); - cli(); - if (!(sp = (struct IsdnCardState *) - kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for IsdnCardState(card %d)\n", - cardnr + 1); - restore_flags(flags); - return (0); - } - card->sp = sp; - sp->cardnr = cardnr; - sp->cfg_reg = 0; - sp->protocol = card->protocol; - - if ((card->typ > 0) && (card->typ < 31)) { - if (!((1 << card->typ) & SUPORTED_CARDS)) { - printk(KERN_WARNING - "HiSax: Support for %s Card not selected\n", - CardType[card->typ]); - restore_flags(flags); - return (0); - } - } else { - printk(KERN_WARNING - "HiSax: Card Type %d out of range\n", - card->typ); - restore_flags(flags); - return (0); - } - if (!(sp->dlogspace = kmalloc(4096, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for dlogspace(card %d)\n", - cardnr + 1); - restore_flags(flags); - return (0); - } - if (!(sp->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for status_buf(card %d)\n", - cardnr + 1); - kfree(sp->dlogspace); - restore_flags(flags); - return (0); - } - sp->status_read = sp->status_buf; - sp->status_write = sp->status_buf; - sp->status_end = sp->status_buf + HISAX_STATUS_BUFSIZE - 1; - sp->typ = card->typ; - sp->CallFlags = 0; - strcpy(sp->iif.id, id); - sp->iif.channels = 2; - sp->iif.maxbufsize = MAX_DATA_SIZE; - sp->iif.hl_hdrlen = MAX_HEADER_LEN; - sp->iif.features = - ISDN_FEATURE_L2_X75I | - ISDN_FEATURE_L2_HDLC | - ISDN_FEATURE_L2_TRANS | - ISDN_FEATURE_L3_TRANS | -#ifdef CONFIG_HISAX_1TR6 - ISDN_FEATURE_P_1TR6 | -#endif -#ifdef CONFIG_HISAX_EURO - ISDN_FEATURE_P_EURO | -#endif -#ifdef CONFIG_HISAX_NI1 - ISDN_FEATURE_P_NI1 | -#endif - 0; + struct PStack *st = fi->userdata; - sp->iif.command = HiSax_command; - sp->iif.writebuf = NULL; - sp->iif.writecmd = NULL; - sp->iif.writebuf_skb = HiSax_writebuf_skb; - sp->iif.readstat = HiSax_readstatus; - register_isdn(&sp->iif); - sp->myid = sp->iif.channels; - restore_flags(flags); - printk(KERN_NOTICE - "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1, - (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" : - (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" : - (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" : - (card->protocol == ISDN_PTYPE_NI1) ? "NI1" : - "NONE", sp->iif.id, sp->myid); - switch (card->typ) { -#if CARD_TELES0 - case ISDN_CTYPE_16_0: - case ISDN_CTYPE_8_0: - ret = setup_teles0(card); - break; -#endif -#if CARD_TELES3 - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_TELESPCMCIA: - ret = setup_teles3(card); - break; -#endif -#if CARD_AVM_A1 - case ISDN_CTYPE_A1: - ret = setup_avm_a1(card); - break; -#endif -#if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: - ret = setup_elsa(card); - break; -#endif -#if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - ret = setup_ix1micro(card); - break; -#endif - default: - printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n", - card->typ); - ll_unload(sp); - return (0); + FsmChangeState(fi, ST_L1_F7); + st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL); + if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) + FsmDelTimer(&st->l1.timer, 4); + if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) { + if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags)) + FsmDelTimer(&st->l1.timer, 3); + FsmAddTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2); + test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags); } - if (!ret) { - ll_unload(sp); - return (0); - } - if (!(sp->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for isac rcvbuf\n"); - return (1); - } - sp->rcvidx = 0; - sp->tx_skb = NULL; - sp->tx_cnt = 0; - sp->event = 0; - sp->tqueue.next = 0; - sp->tqueue.sync = 0; - sp->tqueue.routine = (void *) (void *) isac_bh; - sp->tqueue.data = sp; - - skb_queue_head_init(&sp->rq); - skb_queue_head_init(&sp->sq); - - sp->stlist = NULL; - sp->ph_active = 0; - sp->dlogflag = 0; - sp->debug = L1_DEB_WARN; -#ifdef DEBUG_MAGIC - sp->magic = 301271; -#endif +} - init_hscxstate(sp, 0); - init_hscxstate(sp, 1); +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; - switch (card->typ) { -#if CARD_TELES0 - case ISDN_CTYPE_16_0: - case ISDN_CTYPE_8_0: - ret = initteles0(sp); - break; -#endif -#if CARD_TELES3 - case ISDN_CTYPE_16_3: - case ISDN_CTYPE_PNP: - case ISDN_CTYPE_TELESPCMCIA: - ret = initteles3(sp); - break; -#endif -#if CARD_AVM_A1 - case ISDN_CTYPE_A1: - ret = initavm_a1(sp); - break; -#endif -#if CARD_ELSA - case ISDN_CTYPE_ELSA: - case ISDN_CTYPE_ELSA_QS1000: - ret = initelsa(sp); - break; -#endif -#if CARD_IX1MICROR2 - case ISDN_CTYPE_IX1MICROR2: - ret = initix1micro(sp); - break; -#endif - default: - ret = 0; - break; + test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) + L1deactivated(st->l1.hardware); + if (st->l1.l1m.state != ST_L1_F6) { + FsmChangeState(fi, ST_L1_F3); + st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL); } - if (!ret) { - closecard(cardnr); - return (0); - } - init_tei(sp, sp->protocol); - CallcNewChan(sp); - ll_run(sp); - return (1); } -void -HiSax_shiftcards(int idx) +static void +l1_timer_act(struct FsmInst *fi, int event, void *arg) { - int i; + struct PStack *st = fi->userdata; + + test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags); + test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags); + L1activated(st->l1.hardware); +} - for (i = idx; i < 15; i++) - memcpy(&cards[i], &cards[i + 1], sizeof(cards[i])); +static void +l1_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags); + test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags); + L1deactivated(st->l1.hardware); + st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL); } -int -HiSax_inithardware(void) +static void +l1_activate(struct FsmInst *fi, int event, void *arg) { - int foundcards = 0; - int i = 0; - int t = ','; - int flg = 0; - char *id; - char *next_id = HiSax_id; - char ids[20]; + struct PStack *st = fi->userdata; + + st->l1.l1hw(st, HW_RESET | REQUEST, NULL); +} - if (strchr(HiSax_id, ',')) - t = ','; - else if (strchr(HiSax_id, '%')) - t = '%'; +static void +l1_activate_no(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; - while (i < nrcards) { - if (cards[i].typ < 1) - break; - id = next_id; - if ((next_id = strchr(id, t))) { - *next_id++ = 0; - strcpy(ids, id); - flg = i + 1; - } else { - next_id = id; - if (flg >= i) - strcpy(ids, id); - else - sprintf(ids, "%s%d", id, i); - } - if (checkcard(i, ids)) { - foundcards++; - i++; - } else { - printk(KERN_WARNING "HiSax: Card %s not installed !\n", - CardType[cards[i].typ]); - if (cards[i].sp) - kfree((void *) cards[i].sp); - cards[i].sp = NULL; - HiSax_shiftcards(i); - } + if ((!test_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) && (!test_bit(FLG_L1_T3RUN, &st->l1.Flags))) { + test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags); + L1deactivated(st->l1.hardware); } - return foundcards; } -void -HiSax_closehardware(void) +static struct FsmNode L1DFnList[] HISAX_INITDATA = { - int i; - long flags; + {ST_L1_F3, EV_PH_ACTIVATE, l1_activate}, + {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F3, EV_RESET_IND, l1_reset}, + {ST_L1_F4, EV_RESET_IND, l1_reset}, + {ST_L1_F5, EV_RESET_IND, l1_reset}, + {ST_L1_F6, EV_RESET_IND, l1_reset}, + {ST_L1_F7, EV_RESET_IND, l1_reset}, + {ST_L1_F8, EV_RESET_IND, l1_reset}, + {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F7, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F8, EV_DEACT_IND, l1_deact_req}, + {ST_L1_F3, EV_POWER_UP, l1_power_up}, + {ST_L1_F4, EV_RSYNC_IND, l1_go_F5}, + {ST_L1_F6, EV_RSYNC_IND, l1_go_F8}, + {ST_L1_F7, EV_RSYNC_IND, l1_go_F8}, + {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F3, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F5, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, +}; - save_flags(flags); - cli(); - for (i = 0; i < nrcards; i++) - if (cards[i].sp) { - ll_stop(cards[i].sp); - CallcFreeChan(cards[i].sp); - release_tei(cards[i].sp); - release_irq(i); - closecard(i); - kfree((void *) cards[i].sp); - cards[i].sp = NULL; - } - Isdnl2Free(); - CallcFree(); - restore_flags(flags); -} +#define L1D_FN_COUNT (sizeof(L1DFnList)/sizeof(struct FsmNode)) static void -hscx_l2l1(struct PStack *st, int pr, void *arg) +l1b_activate(struct FsmInst *fi, int event, void *arg) { - struct sk_buff *skb = arg; - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - long flags; - - switch (pr) { - case (PH_DATA): - save_flags(flags); - cli(); - if (hsp->tx_skb) { - skb_queue_tail(&hsp->squeue, skb); - restore_flags(flags); - } else { - restore_flags(flags); - hsp->tx_skb = skb; - hsp->count = 0; - sp->hscx_fill_fifo(hsp); - } - break; - case (PH_DATA_PULLED): - if (hsp->tx_skb) { - printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n"); - break; - } - hsp->tx_skb = skb; - hsp->count = 0; - sp->hscx_fill_fifo(hsp); - break; - case (PH_REQUEST_PULL): - if (!hsp->tx_skb) { - st->l1.requestpull = 0; - st->l1.l1l2(st, PH_PULL_ACK, NULL); - } else - st->l1.requestpull = !0; - break; - } + struct PStack *st = fi->userdata; + FsmChangeState(fi, ST_L1_WAIT_ACT); + FsmAddTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2); } -extern struct IsdnBuffers *tracebuf; static void -hscx_l2l1discardq(struct PStack *st, int pr, void *heldby, - int releasetoo) +l1b_deactivate(struct FsmInst *fi, int event, void *arg) { - struct IsdnCardState *sp = (struct IsdnCardState *) - st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; - struct sk_buff *skb; + struct PStack *st = fi->userdata; -#ifdef DEBUG_MAGIC - if (hsp->magic != 301270) { - printk(KERN_DEBUG "hscx_discardq magic not 301270\n"); - return; - } -#endif - - while ((skb = skb_dequeue(&hsp->squeue))) - dev_kfree_skb(skb, FREE_WRITE); + FsmChangeState(fi, ST_L1_WAIT_DEACT); + FsmAddTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2); } -static int -open_hscxstate(struct IsdnCardState *sp, - int hscx) +static void +l1b_timer_act(struct FsmInst *fi, int event, void *arg) { - struct HscxState *hsp = sp->hs + hscx; - - if (!hsp->init) { - if (!(hsp->rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { - printk(KERN_WARNING - "HiSax: No memory for hscx_rcvbuf\n"); - return (1); - } - skb_queue_head_init(&hsp->rqueue); - skb_queue_head_init(&hsp->squeue); - } - hsp->init = !0; + struct PStack *st = fi->userdata; - hsp->tx_skb = NULL; - hsp->event = 0; - hsp->rcvidx = 0; - hsp->tx_cnt = 0; - return (0); + FsmChangeState(fi, ST_L1_ACTIV); + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); } static void -hscx_manl1(struct PStack *st, int pr, - void *arg) +l1b_timer_deact(struct FsmInst *fi, int event, void *arg) { - struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware; - struct HscxState *hsp = sp->hs + st->l1.hscx; + struct PStack *st = fi->userdata; - switch (pr) { - case (PH_ACTIVATE): - hsp->active = !0; - sp->modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel); - st->l1.l1man(st, PH_ACTIVATE, NULL); - break; - case (PH_DEACTIVATE): - if (!hsp->tx_skb) - sp->modehscx(hsp, 0, 0); - - hsp->active = 0; - break; - } + FsmChangeState(fi, ST_L1_NULL); + st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL); } -int -setstack_hscx(struct PStack *st, struct HscxState *hs) +static struct FsmNode L1BFnList[] HISAX_INITDATA = { - if (open_hscxstate(st->l1.hardware, hs->hscx)) - return (-1); - - st->l1.hscx = hs->hscx; - st->l2.l2l1 = hscx_l2l1; - st->ma.manl1 = hscx_manl1; - st->l2.l2l1discardq = hscx_l2l1discardq; + {ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate}, + {ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act}, + {ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate}, + {ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact}, +}; - st->l1.act_state = 0; - st->l1.requestpull = 0; +#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode)) - hs->st = st; - return (0); +HISAX_INITFUNC(void Isdnl1New(void)) +{ + l1fsm_d.state_count = L1D_STATE_COUNT; + l1fsm_d.event_count = L1_EVENT_COUNT; + l1fsm_d.strEvent = strL1Event; + l1fsm_d.strState = strL1DState; + FsmNew(&l1fsm_d, L1DFnList, L1D_FN_COUNT); + l1fsm_b.state_count = L1B_STATE_COUNT; + l1fsm_b.event_count = L1_EVENT_COUNT; + l1fsm_b.strEvent = strL1Event; + l1fsm_b.strState = strL1BState; + FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT); } -void -HiSax_reportcard(int cardnr) +void Isdnl1Free(void) { - struct IsdnCardState *sp = cards[cardnr].sp; - - printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1); - printk(KERN_DEBUG "HiSax: Type %s\n", CardType[sp->typ]); - printk(KERN_DEBUG "HiSax: debuglevel %x\n", sp->debug); - printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n", - (ulong) & HiSax_reportcard); + FsmFree(&l1fsm_d); + FsmFree(&l1fsm_b); } -#ifdef L2FRAME_DEBUG /* psa */ - -char * -l2cmd(u_char cmd) +static void +dch_l2l1(struct PStack *st, int pr, void *arg) { - switch (cmd & ~0x10) { - case 1: - return "RR"; - case 5: - return "RNR"; - case 9: - return "REJ"; - case 0x6f: - return "SABME"; - case 0x0f: - return "DM"; - case 3: - return "UI"; - case 0x43: - return "DISC"; - case 0x63: - return "UA"; - case 0x87: - return "FRMR"; - case 0xaf: - return "XID"; + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + + switch (pr) { + case (PH_DATA | REQUEST): + case (PH_PULL | REQUEST): + case (PH_PULL |INDICATION): + st->l1.l1hw(st, pr, arg); + break; + case (PH_ACTIVATE | REQUEST): + if (cs->debug) + debugl1(cs, "PH_ACTIVATE_REQ %s", + strL1DState[st->l1.l1m.state]); + if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) + st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL); + else { + test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags); + FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg); + } + break; + case (PH_TESTLOOP | REQUEST): + if (1 & (long) arg) + debugl1(cs, "PH_TEST_LOOP B1"); + if (2 & (long) arg) + debugl1(cs, "PH_TEST_LOOP B2"); + if (!(3 & (long) arg)) + debugl1(cs, "PH_TEST_LOOP DISABLED"); + st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg); + break; default: - if (!(cmd & 1)) - return "I"; - else - return "invalid command"; + if (cs->debug) + debugl1(cs, "dch_l2l1 msg %04X unhandled", pr); + break; } } -static char tmp[20]; +void +l1_msg(struct IsdnCardState *cs, int pr, void *arg) { + struct PStack *st; -char * -l2frames(u_char * ptr) -{ - switch (ptr[2] & ~0x10) { - case 1: - case 5: - case 9: - sprintf(tmp, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1); - break; - case 0x6f: - case 0x0f: - case 3: - case 0x43: - case 0x63: - case 0x87: - case 0xaf: - sprintf(tmp, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4); - break; - default: - if (!(ptr[2] & 1)) { - sprintf(tmp, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1); + st = cs->stlist; + + while (st) { + switch(pr) { + case (HW_RESET | INDICATION): + FsmEvent(&st->l1.l1m, EV_RESET_IND, arg); break; - } else - return "invalid command"; + case (HW_DEACTIVATE | CONFIRM): + FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg); + break; + case (HW_DEACTIVATE | INDICATION): + FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg); + break; + case (HW_POWERUP | CONFIRM): + FsmEvent(&st->l1.l1m, EV_POWER_UP, arg); + break; + case (HW_RSYNC | INDICATION): + FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg); + break; + case (HW_INFO2 | INDICATION): + FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg); + break; + case (HW_INFO4_P8 | INDICATION): + case (HW_INFO4_P10 | INDICATION): + FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg); + break; + default: + if (cs->debug) + debugl1(cs, "l1msg %04X unhandled", pr); + break; + } + st = st->next; } +} - - return tmp; +void +l1_msg_b(struct PStack *st, int pr, void *arg) { + switch(pr) { + case (PH_ACTIVATE | REQUEST): + FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL); + break; + case (PH_DEACTIVATE | REQUEST): + FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL); + break; + } } void -Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir) +setstack_HiSax(struct PStack *st, struct IsdnCardState *cs) { - char tmp[132]; - u_char *ptr; - - ptr = skb->data; - - if (ptr[0] & 1 || !(ptr[1] & 1)) - debugl1(sp, "Addres not LAPD"); - else { - sprintf(tmp, "%s %s: %s%c (sapi %d, tei %d)", - (dir ? "<-" : "->"), buf, l2frames(ptr), - ((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1); - debugl1(sp, tmp); - } + st->l1.hardware = cs; + st->protocol = cs->protocol; + st->l1.l1m.fsm = &l1fsm_d; + st->l1.l1m.state = ST_L1_F3; + st->l1.l1m.debug = cs->debug; + st->l1.l1m.userdata = st; + st->l1.l1m.userint = 0; + st->l1.l1m.printdebug = l1m_debug; + FsmInitTimer(&st->l1.l1m, &st->l1.timer); + setstack_tei(st); + setstack_manager(st); + st->l1.stlistp = &(cs->stlist); + st->l2.l2l1 = dch_l2l1; + st->l1.Flags = 0; + cs->setstack_d(st, cs); } -#endif +void +setstack_l1_B(struct PStack *st) +{ + struct IsdnCardState *cs = st->l1.hardware; + + st->l1.l1m.fsm = &l1fsm_b; + st->l1.l1m.state = ST_L1_NULL; + st->l1.l1m.debug = cs->debug; + st->l1.l1m.userdata = st; + st->l1.l1m.userint = 0; + st->l1.l1m.printdebug = l1m_debug; + st->l1.Flags = 0; + FsmInitTimer(&st->l1.l1m, &st->l1.timer); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl1.h linux/drivers/isdn/hisax/isdnl1.h --- v2.0.35/linux/drivers/isdn/hisax/isdnl1.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/isdnl1.h Sun Nov 15 10:32:58 1998 @@ -1,51 +1,58 @@ -/* $Id: isdnl1.h,v 1.4 1997/04/06 22:55:52 keil Exp $ - * +/* $Id: isdnl1.h,v 1.4.2.7 1998/11/03 00:06:55 keil Exp $ + * $Log: isdnl1.h,v $ - * Revision 1.4 1997/04/06 22:55:52 keil - * Using SKB's + * Revision 1.4.2.7 1998/11/03 00:06:55 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.4.2.6 1998/09/30 22:20:04 keil + * Cosmetics + * + * Revision 1.4.2.5 1998/09/27 13:06:28 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.4.2.4 1998/05/27 18:05:49 keil + * HiSax 3.0 + * + * Revision 1.4.2.3 1997/12/01 09:09:08 keil + * more l1 debug + * + * Revision 1.4.2.2 1997/11/15 18:50:40 keil + * new common init function * - * Revision 1.3 1996/12/08 19:41:55 keil - * L2FRAME_DEBUG + * Revision 1.4.2.1 1997/10/17 22:13:58 keil + * update to last hisax version * - * Revision 1.2 1996/10/27 22:26:27 keil - * ISAC/HSCX version functions + * Revision 2.2 1997/07/30 17:11:09 keil + * L1deactivated exported * - * Revision 1.1 1996/10/13 20:03:47 keil - * Initial revision + * Revision 2.1 1997/07/27 21:43:58 keil + * new l1 interface * + * Revision 2.0 1997/06/26 11:02:55 keil + * New Layer and card interface * * */ - -#define L2FRAME_DEBUG - -/* DEBUG Level */ - -#define L1_DEB_WARN 0x01 -#define L1_DEB_INTSTAT 0x02 -#define L1_DEB_ISAC 0x04 -#define L1_DEB_ISAC_FIFO 0x08 -#define L1_DEB_HSCX 0x10 -#define L1_DEB_HSCX_FIFO 0x20 -#define L1_DEB_LAPD 0x40 - - -#define ISAC_RCVBUFREADY 0 -#define ISAC_XMTBUFREADY 1 -#define ISAC_PHCHANGE 2 - -#define HSCX_RCVBUFREADY 0 -#define HSCX_XMTBUFREADY 1 - -extern void debugl1(struct IsdnCardState *sp, char *msg); -extern char *HscxVersion(u_char v); -extern char *ISACVersion(u_char v); -extern void hscx_sched_event(struct HscxState *hsp, int event); -extern void isac_sched_event(struct IsdnCardState *sp, int event); -extern void isac_new_ph(struct IsdnCardState *sp); -extern get_irq(int cardnr, void *routine); +#define D_RCVBUFREADY 0 +#define D_XMTBUFREADY 1 +#define D_L1STATECHANGE 2 +#define D_CLEARBUSY 3 +#define D_RX_MON0 4 +#define D_RX_MON1 5 +#define D_TX_MON0 6 +#define D_TX_MON1 7 + +#define B_RCVBUFREADY 0 +#define B_XMTBUFREADY 1 + +extern void debugl1(struct IsdnCardState *cs, char *fmt, ...); +extern void DChannel_proc_xmt(struct IsdnCardState *cs); +extern void DChannel_proc_rcv(struct IsdnCardState *cs); +extern void l1_msg(struct IsdnCardState *cs, int pr, void *arg); +extern void l1_msg_b(struct PStack *st, int pr, void *arg); #ifdef L2FRAME_DEBUG -extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir); +extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir); #endif diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c --- v2.0.35/linux/drivers/isdn/hisax/isdnl2.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/isdnl2.c Sun Nov 15 10:32:58 1998 @@ -1,62 +1,75 @@ -/* $Id: isdnl2.c,v 1.10 1997/05/06 09:38:13 keil Exp $ +/* $Id: isdnl2.c,v 1.10.2.11 1998/11/03 00:06:57 keil Exp $ * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * * Thanks to Jan den Ouden * Fritz Elfert * * $Log: isdnl2.c,v $ - * Revision 1.10 1997/05/06 09:38:13 keil - * Bugfixes: - clear ack queue entries after resend - * - acknowlege each frame to linklevel - * - UA for SABM is Response, not command - * - only RR was send as supervisor frame (X.75 hangs after a - * sequence error) + * Revision 1.10.2.11 1998/11/03 00:06:57 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.10.2.10 1998/09/27 13:06:30 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.10.2.9 1998/06/19 15:17:56 keil + * fix LAPB tx_cnt for none I-frames + * + * Revision 1.10.2.8 1998/06/18 23:12:05 keil + * LAPB bugfix * - * Revision 1.9 1997/04/07 23:02:11 keil - * missing braces + * Revision 1.10.2.7 1998/05/27 18:05:51 keil + * HiSax 3.0 * - * Revision 1.8 1997/04/06 22:59:59 keil - * Using SKB's; changing function names; some minor changes + * Revision 1.10.2.6 1998/05/26 10:36:57 keil + * fixes from certification * - * Revision 1.7 1997/02/09 00:25:44 keil - * new interface handling, one interface per card + * Revision 1.10.2.5 1998/03/07 23:15:31 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.6 1997/01/21 22:23:42 keil - * D-channel log changed + * Revision 1.10.2.4 1998/01/27 22:44:38 keil + * fixed window size calculation * - * Revision 1.5 1997/01/04 13:47:06 keil - * handling of MDL_REMOVE added (Thanks to Sim Yskes) + * Revision 1.10.2.3 1997/11/15 18:54:03 keil + * cosmetics * - * Revision 1.4 1996/12/08 19:51:51 keil - * many fixes from Pekka Sarnila + * Revision 1.10.2.2 1997/10/17 22:13:59 keil + * update to last hisax version * - * Revision 1.3 1996/11/05 19:39:12 keil - * X.75 bugfixes Thank to Martin Maurer + * Revision 2.2 1997/07/31 11:49:05 keil + * Eroor handling for no TEI assign * - * Revision 1.2 1996/10/30 10:20:58 keil - * X.75 answer of SABMX fixed to response address (AVM X.75 problem) + * Revision 2.1 1997/07/27 21:34:38 keil + * cosmetics * - * Revision 1.1 1996/10/13 20:04:54 keil - * Initial revision + * Revision 2.0 1997/06/26 11:07:29 keil + * New q.921 and X.75 Layer2 * * + * Old log removed KKe * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl2.h" -const char *l2_revision = "$Revision: 1.10 $"; +const char *l2_revision = "$Revision: 1.10.2.11 $"; -static void l2m_debug(struct FsmInst *fi, char *s); +static void l2m_debug(struct FsmInst *fi, char *fmt, ...); +static struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; enum { ST_L2_1, + ST_L2_2, ST_L2_3, ST_L2_4, ST_L2_5, @@ -70,6 +83,7 @@ static char *strL2State[] = { "ST_L2_1", + "ST_L2_2", "ST_L2_3", "ST_L2_4", "ST_L2_5", @@ -81,53 +95,51 @@ enum { EV_L2_UI, EV_L2_SABMX, - EV_L2_UA, EV_L2_DISC, - EV_L2_I, - EV_L2_RR, - EV_L2_REJ, + EV_L2_DM, + EV_L2_UA, EV_L2_FRMR, + EV_L2_SUPER, + EV_L2_I, EV_L2_DL_DATA, + EV_L2_ACK_PULL, + EV_L2_DL_UNIT_DATA, EV_L2_DL_ESTABLISH, + EV_L2_DL_RELEASE, EV_L2_MDL_ASSIGN, EV_L2_MDL_REMOVE, - EV_L2_DL_UNIT_DATA, - EV_L2_DL_RELEASE, - EV_L2_MDL_NOTEIPROC, + EV_L2_MDL_ERROR, + EV_L1_DEACTIVATE, EV_L2_T200, - EV_L2_ACK_PULL, EV_L2_T203, - EV_L2_RNR, }; -#define L2_EVENT_COUNT (EV_L2_RNR+1) +#define L2_EVENT_COUNT (EV_L2_T203+1) static char *strL2Event[] = { "EV_L2_UI", "EV_L2_SABMX", - "EV_L2_UA", "EV_L2_DISC", - "EV_L2_I", - "EV_L2_RR", - "EV_L2_REJ", + "EV_L2_DM", + "EV_L2_UA", "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNIT_DATA", "EV_L2_DL_ESTABLISH", + "EV_L2_DL_RELEASE", "EV_L2_MDL_ASSIGN", "EV_L2_MDL_REMOVE", - "EV_L2_DL_UNIT_DATA", - "EV_L2_DL_RELEASE", - "EV_L2_MDL_NOTEIPROC", + "EV_L2_MDL_ERROR", + "EV_L1_DEACTIVATE", "EV_L2_T200", - "EV_L2_ACK_PULL", "EV_L2_T203", - "EV_L2_RNR", }; -int errcount = 0; - -static int l2addrsize(struct Layer2 *tsp); +static int l2addrsize(struct Layer2 *l2); static void InitWin(struct Layer2 *l2) @@ -143,7 +155,6 @@ { int i, cnt = 0; - for (i = 0; i < MAX_WINDOW; i++) { if (l2->windowar[i]) { cnt++; @@ -155,55 +166,51 @@ printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt); } -static int +inline int cansend(struct PStack *st) { int p1; - p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8); - return (st->l2.vs != p1); + p1 = st->l2.vs - st->l2.va; + if (p1 < 0) + p1 += (test_bit(FLG_MOD128, &st->l2.flag) ? 128 : 8); + return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag)); } -static void -discard_i_queue(struct PStack *st) +inline void +clear_exception(struct Layer2 *l2) { - struct sk_buff *skb; - - while ((skb = skb_dequeue(&st->l2.i_queue))) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - } + 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); } -int -l2headersize(struct Layer2 *tsp, int ui) +inline int +l2headersize(struct Layer2 *l2, int ui) { - return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1)); + return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1)); } -int -l2addrsize(struct Layer2 *tsp) +inline int +l2addrsize(struct Layer2 *l2) { - return (tsp->laptype == LAPD ? 2 : 1); + return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); } static int -sethdraddr(struct Layer2 *tsp, - u_char * header, int rsp) +sethdraddr(struct Layer2 *l2, u_char * header, int rsp) { u_char *ptr = header; - int crbit; + int crbit = rsp; - if (tsp->laptype == LAPD) { - crbit = rsp; - if (!tsp->orig) - crbit = !crbit; - *ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0); - *ptr++ = (tsp->tei << 1) | 1; + if (test_bit(FLG_LAPD, &l2->flag)) { + *ptr++ = (l2->sap << 2) | (rsp ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; return (2); } else { - crbit = rsp; - if (tsp->orig) + if (test_bit(FLG_ORIG, &l2->flag)) crbit = !crbit; if (crbit) *ptr++ = 1; @@ -213,82 +220,118 @@ } } -static void -enqueue_ui(struct PStack *st, - struct sk_buff *skb) +inline static void +enqueue_super(struct PStack *st, + struct sk_buff *skb) { - st->l2.l2l1(st, PH_DATA, skb); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len; + st->l2.l2l1(st, PH_DATA | REQUEST, skb); } -static void -enqueue_super(struct PStack *st, - struct sk_buff *skb) +#define enqueue_ui(a, b) enqueue_super(a, b) + +inline int +IsUI(u_char * data, int ext) { - st->l2.l2l1(st, PH_DATA, skb); + return ((data[0] & 0xef) == UI); } -static int -legalnr(struct PStack *st, int nr) +inline int +IsUA(u_char * data, int ext) { - struct Layer2 *l2 = &st->l2; - int lnr, lvs; + return ((data[0] & 0xef) == UA); +} - lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8); - lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8); - return (lnr <= lvs); +inline int +IsDM(u_char * data, int ext) +{ + return ((data[0] & 0xef) == DM); } -static void -setva(struct PStack *st, int nr) +inline int +IsDISC(u_char * data, int ext) { - struct Layer2 *l2 = &st->l2; + return ((data[0] & 0xef) == DISC); +} - if (l2->va != nr) { - while (l2->va != nr) { - l2->va = (l2->va + 1) % (l2->extended ? 128 : 8); - dev_kfree_skb(l2->windowar[l2->sow], FREE_WRITE); - l2->windowar[l2->sow] = NULL; - l2->sow = (l2->sow + 1) % l2->window; - if (st->l4.l2writewakeup) - st->l4.l2writewakeup(st); - } - } +inline int +IsRR(u_char * data, int ext) +{ + if (ext) + return (data[0] == RR); + else + return ((data[0] & 0xf) == 1); } -static void -l2s1(struct FsmInst *fi, int event, void *arg) +inline int +IsSFrame(u_char * data, int ext) { - struct PStack *st = fi->userdata; + register u_char d = *data; + + if (!ext) + d &= 0xf; + return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c)); +} - st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces); - FsmChangeState(fi, ST_L2_3); +inline int +IsSABMX(u_char * data, int ext) +{ + u_char d = data[0] & ~0x10; + + return (ext ? d == SABME : d == SABM); } -static void -l2_send_ui(struct FsmInst *fi, int event, void *arg) +inline int +IsREJ(u_char * data, int ext) { - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - u_char header[MAX_HEADER_LEN]; - int i; + return (ext ? data[0] == REJ : (data[0] & 0xf) == REJ); +} - i = sethdraddr(&(st->l2), header, CMD); - header[i++] = UI; - memcpy(skb_push(skb, i), header, i); - enqueue_ui(st, skb); +inline int +IsFRMR(u_char * data, int ext) +{ + return ((data[0] & 0xef) == FRMR); +} + +inline int +IsRNR(u_char * data, int ext) +{ + return (ext ? data[0] == RNR : (data[0] & 0xf) == RNR); +} + +static int +legalnr(struct PStack *st, int nr) +{ + struct Layer2 *l2 = &st->l2; + int lnr, lvs; + + lvs = (l2->vs >= l2->va) ? l2->vs : + (l2->vs + (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8)); + lnr = (nr >= l2->va) ? nr : (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + return (lnr <= lvs); } static void -l2_receive_ui(struct FsmInst *fi, int event, void *arg) +setva(struct PStack *st, int nr) { - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; + struct Layer2 *l2 = &st->l2; + int len; - skb_pull(skb, l2headersize(&st->l2, 1)); - st->l2.l2l3(st, DL_UNIT_DATA, skb); + while (l2->va != nr) { + l2->va = (l2->va + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + len = l2->windowar[l2->sow]->len; + if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type) + len = -1; + dev_kfree_skb(l2->windowar[l2->sow], FREE_WRITE); + l2->windowar[l2->sow] = NULL; + l2->sow = (l2->sow + 1) % l2->window; + if (st->lli.l2writewakeup && (len >=0)) + st->lli.l2writewakeup(st, len); + } } -inline void +static void send_uframe(struct PStack *st, u_char cmd, u_char cr) { struct sk_buff *skb; @@ -306,53 +349,163 @@ enqueue_super(st, skb); } +inline u_char +get_PollFlag(struct PStack * st, struct sk_buff * skb) +{ + return (skb->data[l2addrsize(&(st->l2))] & 0x10); +} + +inline void +FreeSkb(struct sk_buff *skb) +{ + dev_kfree_skb(skb, FREE_READ); +} + + +inline u_char +get_PollFlagFree(struct PStack *st, struct sk_buff *skb) +{ + u_char PF; + + PF = get_PollFlag(st, skb); + FreeSkb(skb); + return (PF); +} + static void establishlink(struct FsmInst *fi) { struct PStack *st = fi->userdata; u_char cmd; - FsmChangeState(fi, ST_L2_5); + clear_exception(&st->l2); st->l2.rc = 0; + cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10; + send_uframe(st, cmd, CMD); + FsmDelTimer(&st->l2.t203, 1); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 1); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + FsmChangeState(fi, ST_L2_5); +} + +static void +l2_mdl_error(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct PStack *st = fi->userdata; - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 1"); + switch (event) { + case EV_L2_UA: + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C'); + else + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D'); + break; + case EV_L2_DM: + if (get_PollFlagFree(st, skb)) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B'); + else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } + break; + } +} +static void +l2_dl_establish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + int state = fi->state; - cmd = (st->l2.extended ? SABME : SABM) | 0x10; - send_uframe(st, cmd, CMD); + + if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { + FsmChangeState(fi, ST_L2_4); + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); + } else { + FsmChangeState(fi, ST_L2_3); + if (state == ST_L2_1) + st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); + } } static void -l2_establish(struct FsmInst *fi, int event, void *arg) +l2_send_ui(struct PStack *st) { - establishlink(fi); + struct sk_buff *skb; + u_char header[MAX_HEADER_LEN]; + int i; + + i = sethdraddr(&(st->l2), header, CMD); + header[i++] = UI; + while ((skb = skb_dequeue(&st->l2.ui_queue))) { + memcpy(skb_push(skb, i), header, i); + enqueue_ui(st, skb); + } } static void -l2_send_disconn(struct FsmInst *fi, int event, void *arg) +l2_put_ui(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - - FsmChangeState(fi, ST_L2_6); + struct sk_buff *skb = arg; - FsmDelTimer(&st->l2.t203_timer, 1); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 2); - st->l2.t200_running = 0; + skb_queue_tail(&st->l2.ui_queue, skb); + if (fi->state == ST_L2_1) { + FsmChangeState(fi, ST_L2_2); + st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL); } - st->l2.rc = 0; - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 2"); + if (fi->state > ST_L2_3) + l2_send_ui(st); +} + +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + skb_pull(skb, l2headersize(&st->l2, 1)); + if (skb->len > st->l2.maxlen) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O'); + FreeSkb(skb); + } else + st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb); +} - if (!((chanp->impair == 2) && (st->l2.laptype == LAPB))) - send_uframe(st, DISC | 0x10, CMD); +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (fi->state != ST_L2_4) + discard_queue(&st->l2.i_queue); + if (fi->state != ST_L2_5) + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); +} + +static void +l2_dl_release(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; - discard_i_queue(st); + if (fi->state == ST_L2_4) { + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + return; + } else if (fi->state == ST_L2_5) { + test_and_set_bit(FLG_PEND_REL, &st->l2.flag); + return; + } + discard_queue(&st->l2.i_queue); + FsmChangeState(fi, ST_L2_6); + st->l2.rc = 0; + send_uframe(st, DISC | 0x10, CMD); + FsmDelTimer(&st->l2.t203, 1); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 2); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } static void @@ -360,46 +513,59 @@ { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int est = 1, state; + int est = 1, state, rsp; u_char PollFlag; state = fi->state; - - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - - if (ST_L2_4 != state) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; + if (rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlagFree(st, skb); + if (ST_L2_6 == state) { + send_uframe(st, DM | PollFlag, RSP); + return; + } else + send_uframe(st, UA | PollFlag, RSP); + if (ST_L2_5 == state) + return; + if (ST_L2_4 != state) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F'); if (st->l2.vs != st->l2.va) { - discard_i_queue(st); + discard_queue(&st->l2.i_queue); est = 1; } else est = 0; - + } + clear_exception(&st->l2); st->l2.vs = 0; st->l2.va = 0; st->l2.vr = 0; st->l2.sow = 0; - if (ST_L2_7 != state) - FsmChangeState(fi, ST_L2_7); - - send_uframe(st, UA | PollFlag, RSP); - - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 15); - st->l2.t200_running = 0; - } - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 3"); + FsmChangeState(fi, ST_L2_7); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3); if (est) - st->l2.l2man(st, DL_ESTABLISH, NULL); + st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL); if (ST_L2_8 == state) if (skb_queue_len(&st->l2.i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } static void @@ -407,87 +573,184 @@ { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - struct Channel *chanp = st->l4.userdata; - u_char PollFlag; - - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + u_char PollFlag, cmd = UA; + int state, rel = 1, cst = 1, rsp; - FsmChangeState(fi, ST_L2_4); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - FsmDelTimer(&st->l2.t203_timer, 3); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 4); - st->l2.t200_running = 0; + if (rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlagFree(st, skb); + if ((state == ST_L2_4) || (state == ST_L2_5)) { + rel = 0; + cst = 0; + cmd = DM; + } else if (state == ST_L2_6) { + rel = 0; + cst = 0; + } + if (cst) { + FsmChangeState(fi, ST_L2_4); + FsmDelTimer(&st->l2.t203, 3); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + } + send_uframe(st, cmd | PollFlag, RSP); + if (rel) { + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); } - if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) - send_uframe(st, UA | PollFlag, RSP); - - st->l2.l2man(st, DL_RELEASE, NULL); } + static void -l2_got_st4_disc(struct FsmInst *fi, int event, void *arg) +l2_got_ua(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - struct Channel *chanp = st->l4.userdata; + int pr=-1; u_char PollFlag; + int state,rsp; - skb_pull(skb, l2addrsize(&(st->l2))); - PollFlag = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - if (!((chanp->impair == 1) && (st->l2.laptype == LAPB))) - send_uframe(st, DM | (PollFlag ? 0x10 : 0x0), RSP); + if (!rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlag(st, skb); + if (!PollFlag) { + l2_mdl_error(fi, event, arg); + return; + } + FreeSkb(skb); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + if (fi->state == ST_L2_5) { + if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) { + discard_queue(&st->l2.i_queue); + st->l2.rc = 0; + send_uframe(st, DISC | 0x10, CMD); + FsmChangeState(fi, ST_L2_6); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } else { + if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) { + pr = DL_ESTABLISH | CONFIRM; + } else if (st->l2.vs != st->l2.va) { + discard_queue(&st->l2.i_queue); + pr = DL_ESTABLISH | INDICATION; + } + st->l2.vs = 0; + st->l2.va = 0; + st->l2.vr = 0; + st->l2.sow = 0; + FsmChangeState(fi, ST_L2_7); + if (pr > -1) + st->l2.l2l3(st, pr, NULL); + } + } else { /* ST_L2_6 */ + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + FsmChangeState(fi, ST_L2_4); + } } static void -l2_got_ua_establish(struct FsmInst *fi, int event, void *arg) +l2_got_dm(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - u_char f; - - skb_pull(skb, l2addrsize(&(st->l2))); - f = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - if (f) { - st->l2.vs = 0; - st->l2.va = 0; - st->l2.vr = 0; - st->l2.sow = 0; - FsmChangeState(fi, ST_L2_7); + u_char PollFlag; + int state,rsp; - FsmDelTimer(&st->l2.t200_timer, 5); - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 4"); + state = fi->state; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &st->l2.flag)) + rsp = !rsp; - st->l2.l2man(st, DL_ESTABLISH, NULL); - } -} - -static void -l2_got_ua_disconn(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - u_char f; - - skb_pull(skb, l2addrsize(&(st->l2))); - f = *skb->data & 0x10; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - if (f) { - FsmDelTimer(&st->l2.t200_timer, 6); - FsmChangeState(fi, ST_L2_4); - st->l2.l2man(st, DL_RELEASE, NULL); + if (!rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + if ((state == ST_L2_7) || (state == ST_L2_8)) + establishlink(fi); + return; + } + if (skb->len != (l2addrsize(&st->l2) + 1)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) + establishlink(fi); + return; + } + PollFlag = get_PollFlagFree(st, skb); + if (!PollFlag) { + if (fi->state == ST_L2_4) { + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + FsmChangeState(fi, ST_L2_5); + } else if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E'); + establishlink(fi); + } + } else { + switch (fi->state) { + case ST_L2_8: + establishlink(fi); + case ST_L2_7: + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B'); + break; + case ST_L2_4: + break; + case ST_L2_5: + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + discard_queue(&st->l2.i_queue); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + FsmChangeState(fi, ST_L2_4); + break; + case ST_L2_6: + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 2); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL); + FsmChangeState(fi, ST_L2_4); + break; + } } } @@ -501,7 +764,7 @@ l2 = &st->l2; i = sethdraddr(l2, tmp, cr); - if (l2->extended) { + if (test_bit(FLG_MOD128, &l2->flag)) { tmp[i++] = typ; tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); } else @@ -516,83 +779,143 @@ } inline void -enquiry_response(struct PStack *st, u_char typ, u_char final) +enquiry_response(struct PStack *st) { - enquiry_cr(st, typ, RSP, final); + if (test_bit(FLG_OWN_BUSY, &st->l2.flag)) + enquiry_cr(st, RNR, RSP, 1); + else + enquiry_cr(st, RR, RSP, 1); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); } inline void -enquiry_command(struct PStack *st, u_char typ, u_char poll) +transmit_enquiry(struct PStack *st) { - enquiry_cr(st, typ, CMD, poll); + if (test_bit(FLG_OWN_BUSY, &st->l2.flag)) + enquiry_cr(st, RNR, CMD, 1); + else + enquiry_cr(st, RR, CMD, 1); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 12); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } + static void nrerrorrecovery(struct FsmInst *fi) { - /* should log error here */ + struct PStack *st = fi->userdata; + + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J'); establishlink(fi); } static void -l2_got_st7_RR(struct FsmInst *fi, int event, void *arg) +invoke_retransmission(struct PStack *st, int nr) +{ + struct Layer2 *l2 = &st->l2; + int p1; + long flags; + + if (l2->vs != nr) { + save_flags(flags); + cli(); + while (l2->vs != nr) { + l2->vs = l2->vs - 1; + if (l2->vs < 0) + l2->vs += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + p1 = l2->vs - l2->va; + if (p1 < 0) + p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + p1 = (p1 + l2->sow) % l2->window; + if (test_bit(FLG_LAPB, &l2->flag)) + st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0); + skb_queue_head(&l2->i_queue, l2->windowar[p1]); + l2->windowar[p1] = NULL; + } + restore_flags(flags); + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + } +} + +static void +l2_got_st7_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; + int PollFlag, nr, rsp, typ = RR; + struct Layer2 *l2 = &st->l2; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + typ = RNR; + } else + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + if (IsREJ(skb->data, test_bit(FLG_MOD128, &l2->flag))) + typ = REJ; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len == 2) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + if (skb->len >2) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + establishlink(fi); + } + FreeSkb(skb); + return; + } } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + if (skb->len == 1) { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + FreeSkb(skb); - if (!((chanp->impair == 4) && (st->l2.laptype == LAPB))) - if ((!rsp) && PollFlag) - enquiry_response(st, RR, PollFlag); - - if (legalnr(st, seq)) { - if (seq == l2->vs) { - setva(st, seq); - FsmDelTimer(&l2->t200_timer, 7); - l2->t200_running = 0; - FsmDelTimer(&l2->t203_timer, 8); - if (FsmAddTimer(&l2->t203_timer, l2->t203, EV_L2_T203, NULL, 5)) - if (l2->l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 5"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else if (l2->va != seq) { - setva(st, seq); - FsmDelTimer(&st->l2.t200_timer, 9); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 6"); - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if ((!rsp) && PollFlag) + enquiry_response(st); + if (rsp && PollFlag) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A'); + if (legalnr(st, nr)) { + if (typ == REJ) { + setva(st, nr); + invoke_retransmission(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 9); + if (FsmAddTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { + setva(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 9); + FsmRestartTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(st, nr); + FsmDelTimer(&st->l2.t203, 9); + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 6); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); } + if (skb_queue_len(&st->l2.i_queue) && (typ == RR)) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } else nrerrorrecovery(fi); if ((fi->userint & LC_FLUSH_WAIT) && rsp && !(skb_queue_len(&st->l2.i_queue))) { fi->userint &= ~LC_FLUSH_WAIT; - st->l2.l2man(st, DL_FLUSH, NULL); + st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL); } } @@ -602,251 +925,216 @@ struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - skb_queue_tail(&st->l2.i_queue, skb); - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0); + if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag))) + skb_queue_tail(&st->l2.i_queue, skb); + if (fi->state == ST_L2_7) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } -static int -icommandreceived(struct FsmInst *fi, int event, void *arg, int *nr) +static void +l2_got_iframe(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; struct sk_buff *skb = arg; - struct IsdnCardState *sp = st->l1.hardware; struct Layer2 *l2 = &(st->l2); - int i, p, seq, wasok; - char str[64]; + int PollFlag, ns, nr, i, rsp; + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + if (rsp) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + FreeSkb(skb); + establishlink(fi); + return; + } i = l2addrsize(l2); - if (l2->extended) { - p = (skb->data[i + 1] & 0x1) == 0x1; - seq = skb->data[i] >> 1; - *nr = (skb->data[i + 1] >> 1) & 0x7f; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len <= (i + 1)) { + FreeSkb(skb); + return; + } else if ((skb->len - i - 1) > l2->maxlen) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O'); + FreeSkb(skb); + establishlink(fi); + return; + } + PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); + ns = skb->data[i] >> 1; + nr = (skb->data[i + 1] >> 1) & 0x7f; } else { - p = (skb->data[i] & 0x10); - seq = (skb->data[i] >> 1) & 0x7; - *nr = (skb->data[i] >> 5) & 0x7; - } - - if (l2->vr == seq) { - wasok = !0; - - l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8); - l2->rejexp = 0; - - if (st->l2.laptype == LAPD) - if (sp->dlogflag) { - LogFrame(st->l1.hardware, skb->data, skb->len); - sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei); - dlogframe(st->l1.hardware, skb->data + l2->ihsize, - skb->len - l2->ihsize, str); - } - if (!((chanp->impair == 3) && (st->l2.laptype == LAPB))) - if (p || (!skb_queue_len(&st->l2.i_queue))) - enquiry_response(st, RR, p); + if (skb->len <= i) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } else if ((skb->len - i) > l2->maxlen) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O'); + FreeSkb(skb); + establishlink(fi); + return; + } + PollFlag = (skb->data[i] & 0x10); + ns = (skb->data[i] >> 1) & 0x7; + nr = (skb->data[i] >> 5) & 0x7; + } + if (test_bit(FLG_OWN_BUSY, &l2->flag)) { + FreeSkb(skb); + enquiry_response(st); + } else if (l2->vr == ns) { + l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (PollFlag) + enquiry_response(st); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); skb_pull(skb, l2headersize(l2, 0)); + st->l2.l2l3(st, DL_DATA | INDICATION, skb); } else { /* n(s)!=v(r) */ - wasok = 0; - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - if (st->l2.rejexp) { - if (p) - if (!((chanp->impair == 3) && (st->l2.laptype == LAPB))) - enquiry_response(st, RR, p); + FreeSkb(skb); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(st); } else { - st->l2.rejexp = !0; - enquiry_command(st, REJ, 1); + enquiry_cr(st, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); } } - return wasok; -} - -static void -l2_got_st7_data(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int nr, wasok; - - wasok = icommandreceived(fi, event, arg, &nr); if (legalnr(st, nr)) { - if (nr == st->l2.vs) { - setva(st, nr); - FsmDelTimer(&st->l2.t200_timer, 10); - st->l2.t200_running = 0; - FsmDelTimer(&st->l2.t203_timer, 11); - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 7)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 5"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); - } else if (nr != st->l2.va) { - setva(st, nr); - FsmDelTimer(&st->l2.t200_timer, 12); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 6"); - - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + setva(st, nr); + if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) { + if (nr == st->l2.vs) { + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 10); + FsmRestartTimer(&st->l2.t203, st->l2.T203, + EV_L2_T203, NULL, 7); + } else if (nr != st->l2.va) { + FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, + NULL, 8); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } } - } else + } else { nrerrorrecovery(fi); + return; + } - if (wasok) - st->l2.l2l3(st, DL_DATA, skb); + if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7)) + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag)) + enquiry_cr(st, RR, RSP, 0); } static void -l2_got_st8_data(struct FsmInst *fi, int event, void *arg) +l2_got_tei(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int nr, wasok; - wasok = icommandreceived(fi, event, arg, &nr); + st->l2.tei = (long) arg; - if (legalnr(st, nr)) { - setva(st, nr); - if (skb_queue_len(&st->l2.i_queue)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &st->l2.flag); } else - nrerrorrecovery(fi); - - if (wasok) - st->l2.l2l3(st, DL_DATA, skb); + FsmChangeState(fi, ST_L2_4); + if (skb_queue_len(&st->l2.ui_queue)) + l2_send_ui(st); } static void -l2_got_tei(struct FsmInst *fi, int event, void *arg) +l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - st->l2.tei = (int) arg; - establishlink(fi); -} - -static void -invoke_retransmission(struct PStack *st, int nr) -{ - struct Layer2 *l2 = &st->l2; - int p1; - - if (l2->vs != nr) { - while (l2->vs != nr) { - - l2->vs = l2->vs - 1; - if (l2->vs < 0) - l2->vs += l2->extended ? 128 : 8; - - p1 = l2->vs - l2->va; - if (p1 < 0) - p1 += l2->extended ? 128 : 8; - p1 = (p1 + l2->sow) % l2->window; - - skb_queue_head(&l2->i_queue, l2->windowar[p1]); - l2->windowar[p1] = NULL; - } - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + } else if (st->l2.rc == st->l2.N200) { + FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); + discard_queue(&st->l2.i_queue); + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G'); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + } else { + st->l2.rc++; + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) + | 0x10, CMD); } } static void -l2_got_st7_rej(struct FsmInst *fi, int event, void *arg) +l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) - rsp = !rsp; - - skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + } else if (st->l2.rc == st->l2.N200) { + FsmChangeState(fi, ST_L2_4); + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H'); + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + st->l2.rc++; + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, + NULL, 9); + send_uframe(st, DISC | 0x10, CMD); } - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); - - if ((!rsp) && PollFlag) - enquiry_response(st, RR, PollFlag); - - if (!legalnr(st, seq)) - return; - - setva(st, seq); - invoke_retransmission(st, seq); } static void -l2_no_tei(struct FsmInst *fi, int event, void *arg) -{ - FsmChangeState(fi, ST_L2_4); -} - -static void -l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) +l2_st78_tout_200(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - u_char cmd; - if (st->l2.rc == st->l2.n200) { - FsmChangeState(fi, ST_L2_4); - st->l2.l2tei(st, MDL_VERIFY, (void *) st->l2.tei); - st->l2.l2man(st, DL_RELEASE, NULL); + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &st->l2.flag); + if (fi->state == ST_L2_7) { + st->l2.rc = 0; + FsmChangeState(fi, ST_L2_8); + } + if (st->l2.rc == st->l2.N200) { + establishlink(fi); } else { + transmit_enquiry(st); st->l2.rc++; - - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 7"); - - cmd = (st->l2.extended ? SABME : SABM) | 0x10; - send_uframe(st, cmd, CMD); } } static void -l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct Channel *chanp = st->l4.userdata; - - if (st->l2.rc == st->l2.n200) { - FsmChangeState(fi, ST_L2_4); - st->l2.l2man(st, DL_RELEASE, NULL); - } else { - st->l2.rc++; - - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 8"); - - - if (!((chanp->impair == 2) && (st->l2.laptype == LAPB))) - send_uframe(st, DISC | 0x10, CMD); + if (test_bit(FLG_LAPD, &st->l2.flag) && + test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) { + FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9); + return; } + FsmChangeState(fi, ST_L2_8); + transmit_enquiry(st); + st->l2.rc = 0; } static void l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; - struct sk_buff *skb; + struct sk_buff *skb, *oskb; struct Layer2 *l2 = &st->l2; u_char header[MAX_HEADER_LEN]; int p1, i; @@ -860,7 +1148,7 @@ p1 = l2->vs - l2->va; if (p1 < 0) - p1 += l2->extended ? 128 : 8; + p1 += test_bit(FLG_MOD128, &l2->flag) ? 128 : 8; p1 = (p1 + l2->sow) % l2->window; if (l2->windowar[p1]) { printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", @@ -871,7 +1159,7 @@ i = sethdraddr(&st->l2, header, CMD); - if (l2->extended) { + if (test_bit(FLG_MOD128, &l2->flag)) { header[i++] = l2->vs << 1; header[i++] = l2->vr << 1; l2->vs = (l2->vs + 1) % 128; @@ -879,146 +1167,129 @@ header[i++] = (l2->vr << 5) | (l2->vs << 1); l2->vs = (l2->vs + 1) % 8; } - - memcpy(skb_push(skb, i), header, i); - st->l2.l2l1(st, PH_DATA_PULLED, skb); - if (!st->l2.t200_running) { - FsmDelTimer(&st->l2.t203_timer, 13); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 9"); - - st->l2.t200_running = !0; + p1 = skb->data - skb->head; + if (p1 >= i) + memcpy(skb_push(skb, i), header, i); + else { + printk(KERN_WARNING + "isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); + oskb = skb; + skb = alloc_skb(oskb->len + i, GFP_ATOMIC); + SET_SKB_FREE(skb); + memcpy(skb_put(skb, i), header, i); + memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len); + FreeSkb(oskb); + } + st->l2.l2l1(st, PH_PULL | INDICATION, skb); + test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag); + if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) { + FsmDelTimer(&st->l2.t203, 13); + FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11); } if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); -} - -static void -transmit_enquiry(struct PStack *st) -{ - - enquiry_command(st, RR, 1); - if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 10"); - - st->l2.t200_running = !0; + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); } static void -l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - st->l2.t200_running = 0; - - st->l2.rc = 1; - FsmChangeState(fi, ST_L2_8); - transmit_enquiry(st); -} - -static void -l2_got_st8_rr_rej(struct FsmInst *fi, int event, void *arg) +l2_got_st8_super(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - int PollFlag, seq, rsp; - struct Layer2 *l2; + int PollFlag, nr, rsp, rnr = 0; + struct Layer2 *l2 = &st->l2; - l2 = &st->l2; - if (l2->laptype == LAPD) - rsp = *skb->data & 0x2; - else - rsp = *skb->data == 0x3; - if (l2->orig) + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) rsp = !rsp; - skb_pull(skb, l2addrsize(l2)); - if (l2->extended) { - PollFlag = (skb->data[1] & 0x1) == 0x1; - seq = skb->data[1] >> 1; + + if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + rnr = 1; + } else + test_and_clear_bit(FLG_PEER_BUSY, &l2->flag); + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len == 2) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } else { - PollFlag = (skb->data[0] & 0x10); - seq = (skb->data[0] >> 5) & 0x7; + if (skb->len == 1) { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + establishlink(fi); + return; + } } - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + FreeSkb(skb); if (rsp && PollFlag) { - if (legalnr(st, seq)) { + if (legalnr(st, nr)) { + setva(st, nr); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 7); + FsmDelTimer(&l2->t203, 8); + if (rnr) { + FsmRestartTimer(&l2->t200, l2->T200, + EV_L2_T200, NULL, 14); + test_and_set_bit(FLG_T200_RUN, &st->l2.flag); + } else + FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + invoke_retransmission(st, nr); FsmChangeState(fi, ST_L2_7); - setva(st, seq); - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 14); - st->l2.t200_running = 0; - } - if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13)) - if (st->l2.l2m.debug) - l2m_debug(&st->l2.l2m, "FAT 11"); - - invoke_retransmission(st, seq); - if (skb_queue_len(&l2->i_queue) && cansend(st)) - st->l2.l2l1(st, PH_REQUEST_PULL, NULL); + st->l2.l2l1(st, PH_PULL | REQUEST, NULL); else if (fi->userint & LC_FLUSH_WAIT) { fi->userint &= ~LC_FLUSH_WAIT; - st->l2.l2man(st, DL_FLUSH, NULL); + st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL); } } } else { if (!rsp && PollFlag) - enquiry_response(st, RR, PollFlag); - if (legalnr(st, seq)) { - setva(st, seq); + enquiry_response(st); + if (legalnr(st, nr)) { + setva(st, nr); } } } static void -l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - st->l2.rc = 0; - FsmChangeState(fi, ST_L2_8); - transmit_enquiry(st); -} - -static void -l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) -{ - struct PStack *st = fi->userdata; - - if (st->l2.rc == st->l2.n200) { - establishlink(fi); - } else { - st->l2.rc++; - transmit_enquiry(st); - } -} - -static void l2_got_FRMR(struct FsmInst *fi, int event, void *arg) { struct PStack *st = fi->userdata; struct sk_buff *skb = arg; - char tmp[64]; - skb_pull(skb, l2addrsize(&st->l2)); - if (st->l2.l2m.debug) { - if (st->l2.extended) - sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x", + skb_pull(skb, l2addrsize(&st->l2) + 1); + if (test_bit(FLG_MOD128, &st->l2.flag)) { + if (skb->len < 5) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + else + l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x", skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4]); + } else { + if (skb->len < 3) + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); else - sprintf(tmp, "FRMR information %2x %2x %2x", + l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x", skb->data[0], skb->data[1], skb->data[2]); - - l2m_debug(&st->l2.l2m, tmp); } - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ + (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &st->l2.flag); + } + FreeSkb(skb); } static void @@ -1026,135 +1297,139 @@ { struct PStack *st = fi->userdata; -/*TODO - if( DL_RELEASE.req outstanding ) { - ... issue DL_RELEASE.confirm - } else { - if( fi->state != ST_L2_4 ) { - ... issue DL_RELEASE.indication - } - } - TODO */ - discard_i_queue(st); /* There is no UI queue in layer 2 */ - st->l2.tei = 255; - if (st->l2.t200_running) { - FsmDelTimer(&st->l2.t200_timer, 18); - st->l2.t200_running = 0; - } - FsmDelTimer(&st->l2.t203_timer, 19); - st->l2.l2man(st, DL_RELEASE, NULL); /* TEMP */ + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + st->l2.tei = -1; + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 18); + FsmDelTimer(&st->l2.t203, 19); + if (fi->state != ST_L2_4) + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); FsmChangeState(fi, ST_L2_1); } -inline int -IsUI(u_char * data, int ext) -{ - return ((data[0] & 0xef) == UI); -} - -inline int -IsUA(u_char * data, int ext) -{ - return ((data[0] & 0xef) == UA); -} - -inline int -IsDISC(u_char * data, int ext) -{ - return ((data[0] & 0xef) == DISC); -} - -inline int -IsRR(u_char * data, int ext) -{ - if (ext) - return (data[0] == RR); - else - return ((data[0] & 0xf) == 1); -} - -inline int -IsI(u_char * data, int ext) -{ - return ((data[0] & 0x1) == 0x0); -} - -inline int -IsSABMX(u_char * data, int ext) +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) { - u_char d = data[0] & ~0x10; + struct PStack *st = fi->userdata; + int rel = DL_RELEASE | INDICATION; - return (ext ? d == SABME : d == SABM); -} - -inline int -IsREJ(u_char * data, int ext) -{ - return (ext ? data[0] == REJ : (data[0] & 0xf) == 0x9); -} - -inline int -IsFRMR(u_char * data, int ext) -{ - return ((data[0] & 0xef) == FRMR); -} - -inline int -IsRNR(u_char * data, int ext) -{ - if (ext) - return (data[0] == RNR); - else - return ((data[0] & 0xf) == 5); + + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); + if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) + FsmDelTimer(&st->l2.t200, 18); + FsmDelTimer(&st->l2.t203, 19); + clear_exception(&st->l2); + switch (fi->state) { + case ST_L2_1: + if (!test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + break; + case ST_L2_3: + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + case ST_L2_2: + FsmChangeState(fi, ST_L2_1); + break; + case ST_L2_6: + rel = DL_RELEASE | CONFIRM; + case ST_L2_5: + if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) + rel = DL_RELEASE | CONFIRM; + case ST_L2_7: + case ST_L2_8: + st->l2.l2l3(st, rel, NULL); + FsmChangeState(fi, ST_L2_4); + break; + case ST_L2_4: + if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL); + break; + } + test_and_clear_bit(FLG_PEND_REL, &st->l2.flag); + test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag); } -static struct FsmNode L2FnList[] = +static struct FsmNode L2FnList[] HISAX_INITDATA = { - {ST_L2_1, EV_L2_DL_ESTABLISH, l2s1}, - {ST_L2_1, EV_L2_MDL_NOTEIPROC, l2_no_tei}, - {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, - {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_1, EV_L2_DL_ESTABLISH, l2_dl_establish}, + {ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish}, {ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish}, - {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_7, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_8, EV_L2_DL_ESTABLISH, l2_establish}, + {ST_L2_4, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_5, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_7, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_8, EV_L2_DL_RELEASE, l2_dl_release}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_iqueue}, {ST_L2_7, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_7, EV_L2_DL_RELEASE, l2_send_disconn}, - {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, - {ST_L2_8, EV_L2_DL_RELEASE, l2_send_disconn}, - - {ST_L2_1, EV_L2_UI, l2_receive_ui}, - {ST_L2_4, EV_L2_UI, l2_receive_ui}, + {ST_L2_1, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_2, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_3, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_5, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_6, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_8, EV_L2_DL_UNIT_DATA, l2_put_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, {ST_L2_4, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_4, EV_L2_DISC, l2_got_st4_disc}, - {ST_L2_5, EV_L2_UA, l2_got_ua_establish}, - {ST_L2_6, EV_L2_UA, l2_got_ua_disconn}, - {ST_L2_7, EV_L2_UI, l2_receive_ui}, - {ST_L2_7, EV_L2_DISC, l2_got_disconn}, - {ST_L2_7, EV_L2_I, l2_got_st7_data}, - {ST_L2_7, EV_L2_RR, l2_got_st7_RR}, - {ST_L2_7, EV_L2_REJ, l2_got_st7_rej}, + {ST_L2_5, EV_L2_SABMX, l2_got_SABMX}, + {ST_L2_6, EV_L2_SABMX, l2_got_SABMX}, {ST_L2_7, EV_L2_SABMX, l2_got_SABMX}, - {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_RR, l2_got_st8_rr_rej}, - {ST_L2_8, EV_L2_REJ, l2_got_st8_rr_rej}, {ST_L2_8, EV_L2_SABMX, l2_got_SABMX}, + {ST_L2_4, EV_L2_DISC, l2_got_disconn}, + {ST_L2_5, EV_L2_DISC, l2_got_disconn}, + {ST_L2_6, EV_L2_DISC, l2_got_disconn}, + {ST_L2_7, EV_L2_DISC, l2_got_disconn}, {ST_L2_8, EV_L2_DISC, l2_got_disconn}, + {ST_L2_4, EV_L2_UA, l2_mdl_error}, + {ST_L2_5, EV_L2_UA, l2_got_ua}, + {ST_L2_6, EV_L2_UA, l2_got_ua}, + {ST_L2_7, EV_L2_UA, l2_mdl_error}, + {ST_L2_8, EV_L2_UA, l2_mdl_error}, + {ST_L2_4, EV_L2_DM, l2_got_dm}, + {ST_L2_5, EV_L2_DM, l2_got_dm}, + {ST_L2_6, EV_L2_DM, l2_got_dm}, + {ST_L2_7, EV_L2_DM, l2_got_dm}, + {ST_L2_8, EV_L2_DM, l2_got_dm}, + {ST_L2_1, EV_L2_UI, l2_got_ui}, + {ST_L2_2, EV_L2_UI, l2_got_ui}, + {ST_L2_3, EV_L2_UI, l2_got_ui}, + {ST_L2_4, EV_L2_UI, l2_got_ui}, + {ST_L2_5, EV_L2_UI, l2_got_ui}, + {ST_L2_6, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_UI, l2_got_ui}, + {ST_L2_8, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, - {ST_L2_8, EV_L2_I, l2_got_st8_data}, - + {ST_L2_7, EV_L2_SUPER, l2_got_st7_super}, + {ST_L2_8, EV_L2_SUPER, l2_got_st8_super}, + {ST_L2_7, EV_L2_I, l2_got_iframe}, + {ST_L2_8, EV_L2_I, l2_got_iframe}, {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, - {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st78_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st78_tout_200}, {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, - {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, - - {ST_L2_1, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_3, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_4, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_5, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_6, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, - {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, }; #define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) @@ -1164,40 +1439,69 @@ { struct sk_buff *skb = arg; u_char *datap; - int ret = !0; + int ret = 1, len; switch (pr) { - case (PH_DATA): + case (PH_DATA | INDICATION): datap = skb->data; - datap += l2addrsize(&st->l2); - - if (IsI(datap, st->l2.extended)) + len = l2addrsize(&st->l2); + if (skb->len > len) + datap += len; + else { + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N'); + FreeSkb(skb); + return; + } + if (!(*datap & 1)) /* I-Frame */ ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb); - else if (IsRR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RR, skb); - else if (IsUI(datap, st->l2.extended)) + else if (IsSFrame(datap, test_bit(FLG_MOD128, &st->l2.flag))) + ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb); + else if (IsUI(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb); - else if (IsSABMX(datap, st->l2.extended)) + else if (IsSABMX(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb); - else if (IsUA(datap, st->l2.extended)) + else if (IsUA(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb); - else if (IsDISC(datap, st->l2.extended)) + else if (IsDISC(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb); - else if (IsREJ(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, skb); - else if (IsFRMR(datap, st->l2.extended)) + else if (IsDM(datap, test_bit(FLG_MOD128, &st->l2.flag))) + ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb); + else if (IsFRMR(datap, test_bit(FLG_MOD128, &st->l2.flag))) ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb); - else if (IsRNR(datap, st->l2.extended)) - ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, skb); - + else { + ret = 1; + if ((st->l2.l2m.state == ST_L2_7) || + (st->l2.l2m.state == ST_L2_8)) + establishlink(&st->l2.l2m); + st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L'); + } if (ret) { - SET_SKB_FREE(skb); - dev_kfree_skb(skb, FREE_READ); + FreeSkb(skb); } break; - case (PH_PULL_ACK): + case (PH_PULL | CONFIRM): FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg); break; + case (PH_PAUSE | INDICATION): + test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag); + break; + case (PH_PAUSE | CONFIRM): + test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag); + break; + case (PH_ACTIVATE | CONFIRM): + case (PH_ACTIVATE | INDICATION): + test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag); + if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag)) + FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg); + break; + case (PH_DEACTIVATE | INDICATION): + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag); + FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg); + break; + default: + l2m_debug(&st->l2.l2m, "l2 unknown pr %04x", pr); + break; } } @@ -1205,69 +1509,70 @@ isdnl2_l3l2(struct PStack *st, int pr, void *arg) { switch (pr) { - case (DL_DATA): + case (DL_DATA | REQUEST): if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) { dev_kfree_skb((struct sk_buff *) arg, FREE_READ); } break; - case (DL_UNIT_DATA): + case (DL_UNIT_DATA | REQUEST): if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) { dev_kfree_skb((struct sk_buff *) arg, FREE_READ); } break; - } -} - -static void -isdnl2_manl2(struct PStack *st, int pr, void *arg) -{ - switch (pr) { - case (DL_ESTABLISH): - FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg); + case (DL_ESTABLISH | REQUEST): + if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) { + if (test_bit(FLG_LAPD, &st->l2.flag) || + test_bit(FLG_ORIG, &st->l2.flag)) { + FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg); + } + } else { + if (test_bit(FLG_LAPD, &st->l2.flag) || + test_bit(FLG_ORIG, &st->l2.flag)) { + test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag); + } + st->l2.l2l1(st, PH_ACTIVATE, NULL); + } break; - case (DL_RELEASE): + case (DL_RELEASE | REQUEST): + if (test_bit(FLG_LAPB, &st->l2.flag)) { + st->l2.l2l1(st, PH_DEACTIVATE, NULL); + } FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg); break; - case (MDL_NOTEIPROC): - FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL); - break; - case (DL_FLUSH): + case (DL_FLUSH | REQUEST): (&st->l2.l2m)->userint |= LC_FLUSH_WAIT; break; - } -} - -static void -isdnl2_teil2(struct PStack *st, int pr, void *arg) -{ - switch (pr) { - case (MDL_ASSIGN): + case (MDL_ASSIGN | REQUEST): FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg); break; - case (MDL_REMOVE): + case (MDL_REMOVE | REQUEST): FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg); break; + case (MDL_ERROR | RESPONSE): + FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg); + break; } } void releasestack_isdnl2(struct PStack *st) { - FsmDelTimer(&st->l2.t200_timer, 15); - FsmDelTimer(&st->l2.t203_timer, 16); - discard_i_queue(st); + FsmDelTimer(&st->l2.t200, 15); + FsmDelTimer(&st->l2.t203, 16); + discard_queue(&st->l2.i_queue); + discard_queue(&st->l2.ui_queue); ReleaseWin(&st->l2); } static void -l2m_debug(struct FsmInst *fi, char *s) +l2m_debug(struct FsmInst *fi, char *fmt, ...) { + va_list args; struct PStack *st = fi->userdata; - char tm[32], str[256]; - jiftime(tm, jiffies); - sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s); - HiSax_putstatus(st->l1.hardware, str); + va_start(args, fmt); + VHiSax_putstatus(st->l1.hardware, st->l2.debug_id, fmt, args); + va_end(args); } void @@ -1275,17 +1580,16 @@ { st->l1.l1l2 = isdnl2_l1l2; st->l3.l3l2 = isdnl2_l3l2; - st->ma.manl2 = isdnl2_manl2; - st->ma.teil2 = isdnl2_teil2; - st->l2.uihsize = l2headersize(&st->l2, !0); - st->l2.ihsize = l2headersize(&st->l2, 0); skb_queue_head_init(&st->l2.i_queue); + skb_queue_head_init(&st->l2.ui_queue); InitWin(&st->l2); - st->l2.rejexp = 0; st->l2.debug = 0; st->l2.l2m.fsm = &l2fsm; + if (test_bit(FLG_LAPB, &st->l2.flag)) + st->l2.l2m.state = ST_L2_4; + else st->l2.l2m.state = ST_L2_1; st->l2.l2m.debug = 0; st->l2.l2m.userdata = st; @@ -1293,14 +1597,31 @@ st->l2.l2m.printdebug = l2m_debug; strcpy(st->l2.debug_id, debug_id); - FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer); - FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer); - st->l2.t200_running = 0; + FsmInitTimer(&st->l2.l2m, &st->l2.t200); + FsmInitTimer(&st->l2.l2m, &st->l2.t203); +} + +static void +transl2_l3l2(struct PStack *st, int pr, void *arg) +{ + switch (pr) { + case (DL_DATA | REQUEST): + case (DL_UNIT_DATA | REQUEST): + st->l2.l2l1(st, PH_DATA | REQUEST, arg); + break; + case (DL_ESTABLISH | REQUEST): + st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL); + break; + case (DL_RELEASE | REQUEST): + st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL); + break; + } } void setstack_transl2(struct PStack *st) { + st->l3.l3l2 = transl2_l3l2; } void @@ -1308,8 +1629,8 @@ { } -void -Isdnl2New(void) +HISAX_INITFUNC(void +Isdnl2New(void)) { l2fsm.state_count = L2_STATE_COUNT; l2fsm.event_count = L2_EVENT_COUNT; diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c --- v2.0.35/linux/drivers/isdn/hisax/isdnl3.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/isdnl3.c Sun Nov 15 10:32:58 1998 @@ -1,44 +1,51 @@ -/* $Id: isdnl3.c,v 1.10 1997/04/06 22:54:16 keil Exp $ +/* $Id: isdnl3.c,v 1.10.2.6 1998/11/03 00:07:06 keil Exp $ - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * * Thanks to Jan den Ouden * Fritz Elfert * * $Log: isdnl3.c,v $ - * Revision 1.10 1997/04/06 22:54:16 keil - * Using SKB's + * Revision 1.10.2.6 1998/11/03 00:07:06 keil + * certification related changes + * fixed logging for smaller stack use * - * Revision 1.9 1997/03/25 23:11:25 keil - * US NI-1 protocol + * Revision 1.10.2.5 1998/09/27 13:06:39 keil + * Apply most changes from 2.1.X (HiSax 3.1) * - * Revision 1.8 1997/03/21 18:53:44 keil - * Report no protocol error to syslog too + * Revision 1.10.2.4 1998/05/27 18:05:59 keil + * HiSax 3.0 * - * Revision 1.7 1997/03/17 18:34:38 keil - * fixed oops if no protocol selected during config + * Revision 1.10.2.3 1997/11/15 18:54:09 keil + * cosmetics * - * Revision 1.6 1997/02/16 01:04:08 fritz - * Bugfix: Changed timer handling caused hang with 2.1.X + * Revision 1.10.2.2 1997/10/17 22:14:05 keil + * update to last hisax version * - * Revision 1.5 1997/02/09 00:26:27 keil - * new interface handling, one interface per card - * leased line changes + * Revision 2.1 1997/08/03 14:36:32 keil + * Implement RESTART procedure * - * Revision 1.4 1997/01/27 23:17:44 keil - * delete timers while unloading + * Revision 2.0 1997/07/27 21:15:42 keil + * New Callref based layer3 * - * Revision 1.3 1997/01/21 22:31:12 keil - * new statemachine; L3 timers + * Revision 1.11 1997/06/26 11:11:44 keil + * SET_SKBFREE now on creation of a SKB * - * Revision 1.2 1996/11/05 19:42:04 keil - * using config.h + * Revision 1.10 1997/04/06 22:54:16 keil + * Using SKB's * - * Revision 1.1 1996/10/13 20:04:54 keil - * Initial revision + * Revision 1.9 1997/03/25 23:11:25 keil + * US NI-1 protocol * + * Revision 1.8 1997/03/21 18:53:44 keil + * Report no protocol error to syslog too * + * Remove old logs /KKe * */ #define __NO_VERSION__ @@ -46,42 +53,145 @@ #include "isdnl3.h" #include -const char *l3_revision = "$Revision: 1.10 $"; +const char *l3_revision = "$Revision: 1.10.2.6 $"; -void -l3_debug(struct PStack *st, char *s) +static +struct Fsm l3fsm = +{NULL, 0, 0, NULL, NULL}; + +enum { + ST_L3_LC_REL, + ST_L3_LC_ESTAB_WAIT, + ST_L3_LC_REL_WAIT, + ST_L3_LC_ESTAB, +}; + +#define L3_STATE_COUNT (ST_L3_LC_ESTAB+1) + +static char *strL3State[] = +{ + "ST_L3_LC_REL", + "ST_L3_LC_ESTAB_WAIT", + "ST_L3_LC_REL_WAIT", + "ST_L3_LC_ESTAB", +}; + +enum { + EV_ESTABLISH_REQ, + EV_ESTABLISH_IND, + EV_ESTABLISH_CNF, + EV_RELEASE_REQ, + EV_RELEASE_CNF, + EV_RELEASE_IND, +}; + +#define L3_EVENT_COUNT (EV_RELEASE_IND+1) + +static char *strL3Event[] = +{ + "EV_ESTABLISH_REQ", + "EV_ESTABLISH_IND", + "EV_ESTABLISH_CNF", + "EV_RELEASE_REQ", + "EV_RELEASE_CNF", + "EV_RELEASE_IND", +}; + +static void +l3m_debug(struct FsmInst *fi, char *fmt, ...) { - char str[256], tm[32]; + va_list args; + struct PStack *st = fi->userdata; - jiftime(tm, jiffies); - sprintf(str, "%s Channel %d l3 %s\n", tm, st->l3.channr, s); - HiSax_putstatus(st->l1.hardware, str); + va_start(args, fmt); + VHiSax_putstatus(st->l1.hardware, st->l3.debug_id, fmt, args); + va_end(args); } +u_char * +findie(u_char * p, int size, u_char ie, int wanted_set) +{ + int l, codeset, maincodeset; + u_char *pend = p + size; + /* skip protocol discriminator, callref and message type */ + p++; + l = (*p++) & 0xf; + p += l; + p++; + codeset = 0; + maincodeset = 0; + /* while there are bytes left... */ + while (p < pend) { + if ((*p & 0xf0) == 0x90) { + codeset = *p & 0x07; + if (!(*p & 0x08)) + maincodeset = codeset; + } + if (*p & 0x80) + p++; + else { + if (codeset == wanted_set) { + if (*p == ie) + return (p); + if (*p > ie) + return (NULL); + } + p++; + l = *p++; + p += l; + codeset = maincodeset; + } + } + return (NULL); +} -void -newl3state(struct PStack *st, int state) +int +getcallref(u_char * p) { - char tmp[80]; - - if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "newstate %d --> %d", st->l3.state, state); - l3_debug(st, tmp); + int l, m = 1, cr = 0; + p++; /* prot discr */ + l = 0xf & *p++; /* callref length */ + if (!l) /* dummy CallRef */ + return(-1); + while (l--) { + cr += m * (*p++); + m *= 8; } - st->l3.state = state; + return (cr); +} + +static int OrigCallRef = 0; + +int +newcallref(void) +{ + if (OrigCallRef == 127) + OrigCallRef = 1; + else + OrigCallRef++; + return (OrigCallRef); +} + +void +newl3state(struct l3_process *pc, int state) +{ + if (pc->debug & L3_DEB_STATE) + l3_debug(pc->st, "newstate cr %d %d --> %d", pc->callref, + pc->state, state); + pc->state = state; } static void L3ExpireTimer(struct L3Timer *t) { - t->st->l4.l4l3(t->st, t->event, NULL); + t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc); } void -L3InitTimer(struct PStack *st, struct L3Timer *t) +L3InitTimer(struct l3_process *pc, struct L3Timer *t) { - t->st = st; + t->pc = pc; t->tl.function = (void *) L3ExpireTimer; t->tl.data = (long) t; init_timer(&t->tl); @@ -109,9 +219,9 @@ } void -StopAllL3Timer(struct PStack *st) +StopAllL3Timer(struct l3_process *pc) { - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); } struct sk_buff * @@ -133,9 +243,10 @@ { struct sk_buff *skb = arg; - l3_debug(st, "no protocol"); - if (skb) + HiSax_putstatus(st->l1.hardware, "L3", "no D protocol"); + if (skb) { dev_kfree_skb(skb, FREE_READ); + } } #ifdef CONFIG_HISAX_EURO @@ -150,14 +261,87 @@ extern void setstack_1tr6(struct PStack *st); #endif +struct l3_process +*getl3proc(struct PStack *st, int cr) +{ + struct l3_process *p = st->l3.proc; + + while (p) + if (p->callref == cr) + return (p); + else + p = p->next; + return (NULL); +} + +struct l3_process +*new_l3_process(struct PStack *st, int cr) +{ + struct l3_process *p, *np; + + if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { + printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr); + return (NULL); + } + if (!st->l3.proc) + st->l3.proc = p; + else { + np = st->l3.proc; + while (np->next) + np = np->next; + np->next = p; + } + p->next = NULL; + p->debug = L3_DEB_WARN; + p->callref = cr; + p->state = 0; + p->chan = NULL; + p->st = st; + p->N303 = st->l3.N303; + L3InitTimer(p, &p->timer); + return (p); +}; + +void +release_l3_process(struct l3_process *p) +{ + struct l3_process *np, *pp = NULL; + + if (!p) + return; + np = p->st->l3.proc; + while (np) { + if (np == p) { + StopAllL3Timer(p); + if (pp) + pp->next = np->next; + else + p->st->l3.proc = np->next; + kfree(p); + return; + } + pp = np; + np = np->next; + } + printk(KERN_ERR "HiSax internal L3 error CR(%d) not in list\n", p->callref); + l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref); +}; + void -setstack_isdnl3(struct PStack *st, struct Channel *chanp) +setstack_l3dc(struct PStack *st, struct Channel *chanp) { char tmp[64]; - st->l3.debug = L3_DEB_WARN; - st->l3.channr = chanp->chan; - L3InitTimer(st, &st->l3.timer); + st->l3.proc = NULL; + st->l3.global = NULL; + skb_queue_head_init(&st->l3.squeue); + st->l3.l3m.fsm = &l3fsm; + st->l3.l3m.state = ST_L3_LC_REL; + st->l3.l3m.debug = 1; + st->l3.l3m.userdata = st; + st->l3.l3m.userint = 0; + st->l3.l3m.printdebug = l3m_debug; + strcpy(st->l3.debug_id, "L3DC "); #ifdef CONFIG_HISAX_EURO if (st->protocol == ISDN_PTYPE_EURO) { @@ -175,11 +359,11 @@ } else #endif if (st->protocol == ISDN_PTYPE_LEASED) { - st->l4.l4l3 = no_l3_proto; + st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; - printk(KERN_NOTICE "HiSax: Leased line mode\n"); + printk(KERN_INFO "HiSax: Leased line mode\n"); } else { - st->l4.l4l3 = no_l3_proto; + st->lli.l4l3 = no_l3_proto; st->l2.l2l3 = no_l3_proto; sprintf(tmp, "protocol %s not supported", (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" : @@ -187,15 +371,155 @@ (st->protocol == ISDN_PTYPE_NI1) ? "ni1" : "unknown"); printk(KERN_WARNING "HiSax: %s\n", tmp); - l3_debug(st, tmp); st->protocol = -1; } - st->l3.state = 0; - st->l3.callref = 0; +} + +void +isdnl3_trans(struct PStack *st, int pr, void *arg) { + st->l3.l3l2(st, pr, arg); } void releasestack_isdnl3(struct PStack *st) { - StopAllL3Timer(st); + while (st->l3.proc) + release_l3_process(st->l3.proc); + if (st->l3.global) { + StopAllL3Timer(st->l3.global); + kfree(st->l3.global); + st->l3.global = NULL; + } + discard_queue(&st->l3.squeue); +} + +void +setstack_l3bc(struct PStack *st, struct Channel *chanp) +{ + + st->l3.proc = NULL; + st->l3.global = NULL; + skb_queue_head_init(&st->l3.squeue); + st->l3.l3m.fsm = &l3fsm; + st->l3.l3m.state = ST_L3_LC_REL; + st->l3.l3m.debug = 1; + st->l3.l3m.userdata = st; + st->l3.l3m.userint = 0; + st->l3.l3m.printdebug = l3m_debug; + strcpy(st->l3.debug_id, "L3BC "); + st->lli.l4l3 = isdnl3_trans; +} + +static void +lc_activate(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT); + st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL); +} + +static void +lc_connect(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + + FsmChangeState(fi, ST_L3_LC_ESTAB); + while ((skb = skb_dequeue(&st->l3.squeue))) { + st->l3.l3l2(st, DL_DATA | REQUEST, skb); + } + st->l3.l3l4(st, DL_ESTABLISH | INDICATION, NULL); +} + +static void +lc_release_req(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + if (fi->state == ST_L3_LC_ESTAB_WAIT) + FsmChangeState(fi, ST_L3_LC_REL); + else + FsmChangeState(fi, ST_L3_LC_REL_WAIT); + st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); +} + +static void +lc_release_ind(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + + FsmChangeState(fi, ST_L3_LC_REL); + discard_queue(&st->l3.squeue); + st->l3.l3l4(st, DL_RELEASE | INDICATION, NULL); +} + +/* *INDENT-OFF* */ +static struct FsmNode L3FnList[] HISAX_INITDATA = +{ + {ST_L3_LC_REL, EV_ESTABLISH_REQ, lc_activate}, + {ST_L3_LC_REL, EV_ESTABLISH_IND, lc_connect}, + {ST_L3_LC_REL, EV_ESTABLISH_CNF, lc_connect}, + {ST_L3_LC_ESTAB_WAIT, EV_ESTABLISH_CNF, lc_connect}, + {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_release_req}, + {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_ESTAB, EV_RELEASE_IND, lc_release_ind}, + {ST_L3_LC_ESTAB, EV_RELEASE_REQ, lc_release_req}, + {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_ind}, + {ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate}, +}; +/* *INDENT-ON* */ + +#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode)) + +void +l3_msg(struct PStack *st, int pr, void *arg) +{ + + switch (pr) { + case (DL_DATA | REQUEST): + if (st->l3.l3m.state == ST_L3_LC_ESTAB) { + st->l3.l3l2(st, pr, arg); + } else { + struct sk_buff *skb = arg; + + skb_queue_head(&st->l3.squeue, skb); + FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); + } + break; + case (DL_ESTABLISH | REQUEST): + FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); + break; + case (DL_ESTABLISH | CONFIRM): + FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL); + break; + case (DL_ESTABLISH | INDICATION): + FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL); + break; + case (DL_RELEASE | INDICATION): + FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL); + break; + case (DL_RELEASE | CONFIRM): + FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL); + break; + case (DL_RELEASE | REQUEST): + FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); + break; + } +} + +HISAX_INITFUNC(void +Isdnl3New(void)) +{ + l3fsm.state_count = L3_STATE_COUNT; + l3fsm.event_count = L3_EVENT_COUNT; + l3fsm.strEvent = strL3Event; + l3fsm.strState = strL3State; + FsmNew(&l3fsm, L3FnList, L3_FN_COUNT); +} + +void +Isdnl3Free(void) +{ + FsmFree(&l3fsm); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl3.h linux/drivers/isdn/hisax/isdnl3.h --- v2.0.35/linux/drivers/isdn/hisax/isdnl3.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/isdnl3.h Sun Nov 15 10:32:58 1998 @@ -1,6 +1,22 @@ -/* $Id: isdnl3.h,v 1.3 1997/04/06 22:54:17 keil Exp $ - * +/* $Id: isdnl3.h,v 1.3.2.3 1998/11/03 00:07:08 keil Exp $ + * $Log: isdnl3.h,v $ + * Revision 1.3.2.3 1998/11/03 00:07:08 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.3.2.2 1998/05/27 18:06:02 keil + * HiSax 3.0 + * + * Revision 1.3.2.1 1997/10/17 22:14:08 keil + * update to last hisax version + * + * Revision 2.0 1997/07/27 21:15:42 keil + * New Callref based layer3 + * + * Revision 1.4 1997/06/26 11:20:57 keil + * ? + * * Revision 1.3 1997/04/06 22:54:17 keil * Using SKB's * @@ -24,15 +40,20 @@ #define L3_DEB_CHARGE 0x08 struct stateentry { - int state; - u_char primitive; - void (*rout) (struct PStack *, u_char, void *); + int state; + int primitive; + void (*rout) (struct l3_process *, u_char, void *); }; -extern void l3_debug(struct PStack *st, char *s); -extern void newl3state(struct PStack *st, int state); -extern void L3InitTimer(struct PStack *st, struct L3Timer *t); +#define l3_debug(st, fmt, args...) HiSax_putstatus(st->l1.hardware, "l3 ", fmt, ## args) + +extern void newl3state(struct l3_process *pc, int state); +extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t); extern void L3DelTimer(struct L3Timer *t); -extern int L3AddTimer(struct L3Timer *t, int millisec, int event); -extern void StopAllL3Timer(struct PStack *st); +extern int L3AddTimer(struct L3Timer *t, int millisec, int event); +extern void StopAllL3Timer(struct l3_process *pc); extern struct sk_buff *l3_alloc_skb(int len); +extern struct l3_process *new_l3_process(struct PStack *st, int cr); +extern void release_l3_process(struct l3_process *p); +extern struct l3_process *getl3proc(struct PStack *st, int cr); +extern void l3_msg(struct PStack *st, int pr, void *arg); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/ix1_micro.c linux/drivers/isdn/hisax/ix1_micro.c --- v2.0.35/linux/drivers/isdn/hisax/ix1_micro.c Tue Aug 5 09:00:12 1997 +++ linux/drivers/isdn/hisax/ix1_micro.c Sun Nov 15 10:32:58 1998 @@ -1,4 +1,4 @@ -/* $Id: ix1_micro.c,v 1.3 1997/04/13 19:54:02 keil Exp $ +/* $Id: ix1_micro.c,v 1.3.2.8 1998/04/08 21:58:41 keil Exp $ * ix1_micro.c low level stuff for ITK ix1-micro Rev.2 isdn cards * derived from the original file teles3.c from Karsten Keil @@ -11,6 +11,27 @@ * Beat Doebeli * * $Log: ix1_micro.c,v $ + * Revision 1.3.2.8 1998/04/08 21:58:41 keil + * New init code + * + * Revision 1.3.2.7 1998/02/11 14:21:01 keil + * cosmetic fixes + * + * Revision 1.3.2.6 1998/01/27 22:37:33 keil + * fast io + * + * Revision 1.3.2.5 1997/11/15 18:50:51 keil + * new common init function + * + * Revision 1.3.2.4 1997/10/17 22:14:09 keil + * update to last hisax version + * + * Revision 2.1 1997/07/27 21:47:09 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:50 keil + * New Layer and card interface + * * Revision 1.3 1997/04/13 19:54:02 keil * Change in IRQ check delay for SMP * @@ -54,17 +75,16 @@ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles3.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include extern const char *CardType[]; -const char *ix1_revision = "$Revision: 1.3 $"; +const char *ix1_revision = "$Revision: 1.3.2.8 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) #define SPECIAL_PORT_OFFSET 3 @@ -73,865 +93,247 @@ #define HSCX_COMMAND_OFFSET 2 #define HSCX_DATA_OFFSET 1 -#define ISAC_FIFOSIZE 16 -#define HSCX_FIFOSIZE 16 - #define TIMEOUT 50 static inline u_char -IsacReadReg(unsigned int adr, u_char off) -{ - byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20); - return bytein(adr + ISAC_DATA_OFFSET); -} - -static inline void -IsacWriteReg(unsigned int adr, u_char off, u_char data) -{ - byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20); - byteout(adr + ISAC_DATA_OFFSET, data); -} - -#define HSCX_OFFSET(WhichHscx,offset) \ -( (WhichHscx) ? (offset+0x60) : (offset+0x20) ) - -static inline u_char -HscxReadReg(unsigned int adr, int WhichHscx, u_char off) +readreg(unsigned int ale, unsigned int adr, u_char off) { - byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off)); - return bytein(adr + HSCX_DATA_OFFSET); -} - -static inline void -HscxWriteReg(unsigned int adr, int WhichHscx, u_char off, u_char data) -{ - byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off)); - byteout(adr + HSCX_DATA_OFFSET, data); -} - - -static inline void -IsacReadFifo(unsigned int adr, u_char * data, int size) -{ - byteout(adr + ISAC_COMMAND_OFFSET, 0); - while (size--) - *data++ = bytein(adr + ISAC_DATA_OFFSET); -} - -static void -IsacWriteFifo(unsigned int adr, u_char * data, int size) -{ - byteout(adr + ISAC_COMMAND_OFFSET, 0); - while (size--) { - byteout(adr + ISAC_DATA_OFFSET, *data); - data++; - } -} - -static inline void -HscxReadFifo(unsigned int adr, int WhichHscx, u_char * data, int size) -{ - byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00); - while (size--) - *data++ = bytein(adr + HSCX_DATA_OFFSET); -} + register u_char ret; + long flags; -static void -HscxWriteFifo(unsigned int adr, int WhichHscx, u_char * data, int size) -{ - byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00); - while (size--) { - byteout(adr + HSCX_DATA_OFFSET, *data); - data++; - } + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); } static inline void -waitforCEC(int adr, int WhichHscx) +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { - int to = TIMEOUT; + /* fifo read without cli because it's allready done */ - while ((HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "ix1-Micro: waitforCEC timeout\n"); + byteout(ale, off); + insb(adr, data, size); } static inline void -waitforXFW(int adr, int WhichHscx) -{ - int to = TIMEOUT; - - while ((!(HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "ix1-Micro: waitforXFW timeout\n"); -} - -static inline void -writehscxCMDR(int adr, int WhichHscx, u_char data) +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) { long flags; save_flags(flags); cli(); - waitforCEC(adr, WhichHscx); - HscxWriteReg(adr, WhichHscx, HSCX_CMDR, data); + byteout(ale, off); + byteout(adr, data); restore_flags(flags); } -/* - * fast interrupt here - */ - -static void -hscxreport(struct IsdnCardState *sp, int hscx) -{ - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_EXIR)); -} - -void -ix1micro_report(struct IsdnCardState *sp) +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) { - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", IsacReadReg(sp->isac, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", IsacReadReg(sp->isac, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", IsacReadReg(sp->isac, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); } -/* - * HSCX stuff goes here - */ +/* Interface functions */ -static void -hscx_empty_fifo(struct HscxState *hsp, int count) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - HscxReadFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset)); } static void -hscx_fill_fifo(struct HscxState *hsp) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx], hsp->hscx); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - HscxWriteFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value); } -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!(r & 0x80)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!(r & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80); - } else { - count = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (sp->debug & L1_DEB_HSCX_FIFO) { - sprintf(tmp, "HX Frame %d", count); - debugl1(sp, tmp); - } - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "IX1: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "IX1: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - IsacReadFifo(sp->isac, ptr, count); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - IsacWriteFifo(sp->isac, ptr, count); - IsacWriteReg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.ix1.hscx_ale, + cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0))); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_CIX0, (command << 2) | 3); + writereg(cs->hw.ix1.hscx_ale, + cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value); } +#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data) -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = IsacReadReg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = IsacReadReg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - sp->rcvidx = 0; - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "IX1: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (IsacReadReg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = IsacReadReg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \ + cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { - printk(KERN_WARNING "Teles: Spurious interrupt!\n"); + if (!cs) { + printk(KERN_WARNING "IX1: Spurious interrupt!\n"); return; } - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = IsacReadReg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); + val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40); if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = IsacReadReg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA); if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (stat & 1) { - HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0xFF); - HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0xFF); - HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0x0); - HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0x0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0); + writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0); } if (stat & 2) { - IsacWriteReg(sp->isac, ISAC_MASK, 0xFF); - IsacWriteReg(sp->isac, ISAC_MASK, 0x0); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0); } } - -static void -initisac(struct IsdnCardState *sp) +void +release_io_ix1micro(struct IsdnCardState *cs) { - unsigned int adr = sp->isac; - - /* 16.3 IOM 2 Mode */ - IsacWriteReg(adr, ISAC_MASK, 0xff); - IsacWriteReg(adr, ISAC_ADF2, 0x80); - IsacWriteReg(adr, ISAC_SQXR, 0x2f); - IsacWriteReg(adr, ISAC_SPCR, 0x0); - IsacWriteReg(adr, ISAC_ADF1, 0x2); - IsacWriteReg(adr, ISAC_STCR, 0x70); - IsacWriteReg(adr, ISAC_MODE, 0xc9); - IsacWriteReg(adr, ISAC_TIMR, 0x0); - IsacWriteReg(adr, ISAC_ADF1, 0x0); - IsacWriteReg(adr, ISAC_CMDR, 0x41); - IsacWriteReg(adr, ISAC_CIX0, (1 << 2) | 3); - IsacWriteReg(adr, ISAC_MASK, 0xff); - IsacWriteReg(adr, ISAC_MASK, 0x0); + if (cs->hw.ix1.cfg_reg) + release_region(cs->hw.ix1.cfg_reg, 4); } static void -modehscx(struct HscxState *hs, int mode, int ichan) +ix1_reset(struct IsdnCardState *cs) { - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; + long flags; + int cnt; - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR1, 0x85); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD1, 0xFF); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD2, 0xFF); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RAH2, 0xFF); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XBCH, 0x0); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RLCR, 0x0); - - switch (mode) { - case 0: - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0xff); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0xff); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x84); - break; - case 1: - if (ichan == 0) { - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - } else { - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - } - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0xe4); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41); - break; - case 2: - if (ichan == 0) { - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - } else { - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7); - } - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x8c); - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41); - break; + /* reset isac */ + save_flags(flags); + cnt = 3 * (HZ / 10) + 1; + sti(); + while (cnt--) { + byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1); + HZDELAY(1); /* wait >=10 ms */ } - HscxWriteReg(sp->hscx[hscx], hscx, HSCX_ISTA, 0x00); -} - -void -release_io_ix1micro(struct IsdnCard *card) -{ - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 4); + byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0); + restore_flags(flags); } -static void -clear_pending_ints(struct IsdnCardState *sp) +static int +ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int val; - char tmp[64]; - - val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = HscxReadReg(sp->hscx[1], 1, HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = HscxReadReg(sp->hscx[0], 0, HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = IsacReadReg(sp->isac, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = IsacReadReg(sp->isac, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = IsacReadReg(sp->isac, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = IsacReadReg(sp->isac, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = IsacReadReg(sp->isac, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = IsacReadReg(sp->isac, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); - } - IsacWriteReg(sp->isac, ISAC_MASK, 0); - IsacWriteReg(sp->isac, ISAC_CMDR, 0x41); -} - -int -initix1micro(struct IsdnCardState *sp) -{ - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat.interrupts[sp->irq]; - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &ix1micro_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat.interrupts[sp->irq] > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat.interrupts[sp->irq]); - debugl1(sp, tmp); - if (kstat.interrupts[sp->irq] == sp->counter) { - printk(KERN_WARNING - "ix1-Micro: IRQ(%d) getting no interrupts during init\n", - sp->irq); - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); - } + switch (mt) { + case CARD_RESET: + ix1_reset(cs); + return(0); + case CARD_RELEASE: + release_io_ix1micro(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &ix1micro_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); } - return (ret); + return(0); } -int -setup_ix1micro(struct IsdnCard *card) + +__initfunc(int +setup_ix1micro(struct IsdnCard *card)) { - u_char val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, ix1_revision); - printk(KERN_NOTICE "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp)); - if (sp->typ != ISDN_CTYPE_IX1MICROR2) + printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_IX1MICROR2) return (0); /* IO-Ports */ - sp->isac = sp->hscx[0] = sp->hscx[1] = sp->cfg_reg = card->para[1]; - sp->irq = card->para[0]; - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 4)) { + cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET; + cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET; + cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET; + cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET; + cs->hw.ix1.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (cs->hw.ix1.cfg_reg) { + if (check_region((cs->hw.ix1.cfg_reg), 4)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 4); + cs->hw.ix1.cfg_reg, + cs->hw.ix1.cfg_reg + 4); return (0); } else - request_region(sp->cfg_reg, 4, "ix1micro cfg"); + request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg"); } - /* reset isac */ - save_flags(flags); - val = 3 * (HZ / 10) + 1; - sti(); - while (val--) { - byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 1); - HZDELAY(1); /* wait >=10 ms */ - } - byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 0); - restore_flags(flags); - - printk(KERN_NOTICE - "HiSax: %s config irq:%d io:0x%x\n", - CardType[sp->typ], sp->irq, - sp->cfg_reg); - verA = HscxReadReg(sp->hscx[0], 0, HSCX_VSTR) & 0xf; - verB = HscxReadReg(sp->hscx[1], 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "ix1-Micro: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = IsacReadReg(sp->isac, ISAC_RBCH); - printk(KERN_INFO "ix1-Micro: ISAC %s\n", - ISACVersion(val)); - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + printk(KERN_INFO + "HiSax: %s config irq:%d io:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.ix1.cfg_reg); + ix1_reset(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &ix1_card_msg; + ISACVersion(cs, "ix1-Micro:"); + if (HscxVersion(cs, "ix1-Micro:")) { printk(KERN_WARNING "ix1-Micro: wrong HSCX versions check IO address\n"); - release_io_ix1micro(card); + release_io_ix1micro(cs); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c --- v2.0.35/linux/drivers/isdn/hisax/l3_1tr6.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/l3_1tr6.c Sun Nov 15 10:32:58 1998 @@ -1,44 +1,47 @@ -/* $Id: l3_1tr6.c,v 1.11 1997/04/06 22:54:18 keil Exp $ +/* $Id: l3_1tr6.c,v 1.11.2.6 1998/11/03 00:07:10 keil Exp $ * German 1TR6 D-channel protocol * * Author Karsten Keil (keil@temic-ech.spacenet.de) * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert * - * $Log: l3_1tr6.c,v $ - * Revision 1.11 1997/04/06 22:54:18 keil - * Using SKB's * - * Revision 1.10 1997/03/13 20:37:58 keil - * channel request added + * $Log: l3_1tr6.c,v $ + * Revision 1.11.2.6 1998/11/03 00:07:10 keil + * certification related changes + * fixed logging for smaller stack use * - * Revision 1.9 1997/02/11 01:37:40 keil - * Changed setup-interface (incoming and outgoing) + * Revision 1.11.2.5 1998/10/25 18:16:21 fritz + * Replaced some read-only variables by defines. * - * Revision 1.8 1997/01/27 23:20:21 keil - * report revision only ones + * Revision 1.11.2.4 1998/09/27 13:06:42 keil + * Apply most changes from 2.1.X (HiSax 3.1) * - * Revision 1.7 1997/01/21 22:30:07 keil - * new statemachine; L3 timers + * Revision 1.11.2.3 1998/05/27 18:06:04 keil + * HiSax 3.0 * - * Revision 1.6 1996/12/14 21:07:20 keil - * additional states for CC_REJECT + * Revision 1.11.2.2 1997/11/15 18:54:12 keil + * cosmetics * - * Revision 1.5 1996/12/08 19:55:17 keil - * change CC_REJECT_REQ routine + * Revision 1.11.2.1 1997/10/17 22:14:12 keil + * update to last hisax version * - * Revision 1.4 1996/10/30 10:18:01 keil - * bugfixes in debugging output + * Revision 2.1 1997/08/03 15:28:09 keil + * release L3 empty processes * - * Revision 1.3 1996/10/27 22:15:37 keil - * bugfix reject handling + * Revision 2.0 1997/07/27 21:15:45 keil + * New Callref based layer3 * - * Revision 1.2 1996/10/13 23:08:56 keil - * added missing state for callback reject + * Revision 1.12 1997/06/26 11:11:45 keil + * SET_SKBFREE now on creation of a SKB * - * Revision 1.1 1996/10/13 20:04:55 keil - * Initial revision + * Revision 1.11 1997/04/06 22:54:18 keil + * Using SKB's * + * Old Log removed /KKe * */ @@ -49,16 +52,16 @@ #include extern char *HiSax_getrev(const char *revision); -const char *l3_1tr6_revision = "$Revision: 1.11 $"; +const char *l3_1tr6_revision = "$Revision: 1.11.2.6 $"; #define MsgHead(ptr, cref, mty, dis) \ *ptr++ = dis; \ *ptr++ = 0x1; \ - *ptr++ = cref; \ + *ptr++ = cref ^ 0x80; \ *ptr++ = mty static void -l3_1TR6_message(struct PStack *st, u_char mt, u_char pd) +l3_1TR6_message(struct l3_process *pc, u_char mt, u_char pd) { struct sk_buff *skb; u_char *p; @@ -66,12 +69,39 @@ if (!(skb = l3_alloc_skb(4))) return; p = skb_put(skb, 4); - MsgHead(p, st->l3.callref, mt, pd); - st->l3.l3l2(st, DL_DATA, skb); + MsgHead(p, pc->callref, mt, pd); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg) +{ + StopAllL3Timer(pc); + newl3state(pc, 19); + l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + + dev_kfree_skb(skb, FREE_READ); + l3_1tr6_release_req(pc, 0, NULL); } static void -l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb) +{ + dev_kfree_skb(skb, FREE_READ); + if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, msg); + l3_1tr6_release_req(pc, 0, NULL); +} + +static void +l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[128]; @@ -81,16 +111,13 @@ u_char channel = 0; int l; - - st->l3.callref = st->pa->callref; - MsgHead(p, st->l3.callref, MT_N1_SETUP, PROTO_DIS_N1); - - teln = st->pa->setup.phone; - st->pa->spv = 0; + MsgHead(p, pc->callref, MT_N1_SETUP, PROTO_DIS_N1); + teln = pc->para.setup.phone; + pc->para.spv = 0; if (!isdigit(*teln)) { switch (0x5f & *teln) { case 'S': - st->pa->spv = 1; + pc->para.spv = 1; break; case 'C': channel = 0x08; @@ -103,8 +130,8 @@ channel |= 0x02; break; default: - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "Wrong MSN Code"); + if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "Wrong MSN Code"); break; } teln++; @@ -114,22 +141,22 @@ *p++ = 1; *p++ = channel; } - if (st->pa->spv) { /* SPV ? */ + if (pc->para.spv) { /* SPV ? */ /* NSF SPV */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->setup.si1; /* 0 for all Services */ - *p++ = st->pa->setup.si2; /* 0 for all Services */ + *p++ = pc->para.setup.si1; /* 0 for all Services */ + *p++ = pc->para.setup.si2; /* 0 for all Services */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_Activate; /* aktiviere SPV (default) */ - *p++ = st->pa->setup.si1; /* 0 for all Services */ - *p++ = st->pa->setup.si2; /* 0 for all Services */ + *p++ = pc->para.setup.si1; /* 0 for all Services */ + *p++ = pc->para.setup.si2; /* 0 for all Services */ } - eaz = st->pa->setup.eazmsn; + eaz = pc->para.setup.eazmsn; if (*eaz) { *p++ = WE0_origAddr; *p++ = strlen(eaz) + 1; @@ -149,22 +176,21 @@ /* Codesatz 6 fuer Service */ *p++ = WE6_serviceInd; *p++ = 2; /* len=2 info,info2 */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303); - newl3state(st, 1); - st->l3.l3l2(st, DL_DATA, skb); - + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + newl3state(pc, 1); + l3_msg(pc->st, DL_DATA | REQUEST, skb); } static void -l3_1tr6_setup(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg) { u_char *p; int bcfound = 0; @@ -172,110 +198,141 @@ struct sk_buff *skb = arg; p = skb->data; - st->pa->callref = getcallref(p); - st->l3.callref = 0x80 + st->pa->callref; /* Channel Identification */ p = skb->data; if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - bcfound++; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); + if (p[1] != 1) { + l3_1tr6_error(pc, "setup wrong chanID len", skb); + return; + } + if ((p[2] & 0xf4) != 0x80) { + l3_1tr6_error(pc, "setup wrong WE0_chanID", skb); + return; + } + if ((pc->para.bchannel = p[2] & 0x3)) + bcfound++; + } else { + l3_1tr6_error(pc, "missing setup chanID", skb); + return; + } p = skb->data; if ((p = findie(p, skb->len, WE6_serviceInd, 6))) { - st->pa->setup.si1 = p[2]; - st->pa->setup.si2 = p[3]; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without service indicator"); + pc->para.setup.si1 = p[2]; + pc->para.setup.si2 = p[3]; + } else { + l3_1tr6_error(pc, "missing setup SI", skb); + return; + } p = skb->data; if ((p = findie(p, skb->len, WE0_destAddr, 0))) - iecpy(st->pa->setup.eazmsn, p, 1); + iecpy(pc->para.setup.eazmsn, p, 1); else - st->pa->setup.eazmsn[0] = 0; + pc->para.setup.eazmsn[0] = 0; p = skb->data; if ((p = findie(p, skb->len, WE0_origAddr, 0))) { - iecpy(st->pa->setup.phone, p, 1); + iecpy(pc->para.setup.phone, p, 1); } else - st->pa->setup.phone[0] = 0; + pc->para.setup.phone[0] = 0; p = skb->data; - st->pa->spv = 0; + pc->para.spv = 0; if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) { if ((FAC_SPV == p[3]) || (FAC_Activate == p[3])) - st->pa->spv = 1; + pc->para.spv = 1; } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); /* Signal all services, linklevel takes care of Service-Indicator */ if (bcfound) { - if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) { + if ((pc->para.setup.si1 != 7) && (pc->st->l3.debug & L3_DEB_WARN)) { sprintf(tmp, "non-digital call: %s -> %s", - st->pa->setup.phone, - st->pa->setup.eazmsn); - l3_debug(st, tmp); + pc->para.setup.phone, + pc->para.setup.eazmsn); + l3_debug(pc->st, tmp); } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } + newl3state(pc, 6); + pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); + } else + release_l3_process(pc); } static void -l3_1tr6_setup_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - newl3state(st, 2); + newl3state(pc, 2); if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if (p[1] != 1) { + l3_1tr6_error(pc, "setup_ack wrong chanID len", skb); + return; + } + if ((p[2] & 0xf4) != 0x80) { + l3_1tr6_error(pc, "setup_ack wrong WE0_chanID", skb); + return; + } + pc->para.bchannel = p[2] & 0x3; + } else { + l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb); + return; + } dev_kfree_skb(skb, FREE_READ); - L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); - st->l3.l3l4(st, CC_MORE_INFO, NULL); + L3AddTimer(&pc->timer, T304, CC_T304); + pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void -l3_1tr6_call_sent(struct PStack *st, u_char pr, void *arg) +l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; if ((p = findie(p, skb->len, WE0_chanID, 0))) { - st->pa->bchannel = p[2] & 0x3; - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if (p[1] != 1) { + l3_1tr6_error(pc, "call sent wrong chanID len", skb); + return; + } + if ((p[2] & 0xf4) != 0x80) { + l3_1tr6_error(pc, "call sent wrong WE0_chanID", skb); + return; + } + if ((pc->state == 2) && (pc->para.bchannel != (p[2] & 0x3))) { + l3_1tr6_error(pc, "call sent wrong chanID value", skb); + return; + } + pc->para.bchannel = p[2] & 0x3; + } else { + l3_1tr6_error(pc, "missing call sent WE0_chanID", skb); + return; + } dev_kfree_skb(skb, FREE_READ); - L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310); - newl3state(st, 3); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); + L3AddTimer(&pc->timer, T310, CC_T310); + newl3state(pc, 3); + pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void -l3_1tr6_alert(struct PStack *st, u_char pr, void *arg) +l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - L3DelTimer(&st->l3.timer); /* T304 */ - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); } static void -l3_1tr6_info(struct PStack *st, u_char pr, void *arg) +l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg) { u_char *p; int i, tmpcharge = 0; @@ -289,45 +346,46 @@ tmpcharge *= 10; tmpcharge += a_charge[i] & 0xf; } - if (tmpcharge > st->pa->chargeinfo) { - st->pa->chargeinfo = tmpcharge; - st->l3.l3l4(st, CC_INFO_CHARGE, NULL); + if (tmpcharge > pc->para.chargeinfo) { + pc->para.chargeinfo = tmpcharge; + pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); } - if (st->l3.debug & L3_DEB_CHARGE) { - sprintf(tmp, "charging info %d", st->pa->chargeinfo); - l3_debug(st, tmp); + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); } - } else if (st->l3.debug & L3_DEB_CHARGE) - l3_debug(st, "charging info not found"); - SET_SKB_FREE(skb); + } else if (pc->st->l3.debug & L3_DEB_CHARGE) + l3_debug(pc->st, "charging info not found"); dev_kfree_skb(skb, FREE_READ); } static void -l3_1tr6_info_s2(struct PStack *st, u_char pr, void *arg) +l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); } static void -l3_1tr6_connect(struct PStack *st, u_char pr, void *arg) +l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); /* T310 */ - newl3state(st, 10); - SET_SKB_FREE(skb); + L3DelTimer(&pc->timer); /* T310 */ + if (!findie(skb->data, skb->len, WE6_date, 6)) { + l3_1tr6_error(pc, "missing connect date", skb); + return; + } + newl3state(pc, 10); dev_kfree_skb(skb, FREE_READ); - st->pa->chargeinfo = 0; - st->l3.l3l4(st, CC_SETUP_CNF, NULL); + pc->para.chargeinfo = 0; + pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); } static void -l3_1tr6_rel(struct PStack *st, u_char pr, void *arg) +l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; u_char *p; @@ -335,47 +393,50 @@ p = skb->data; if ((p = findie(p, skb->len, WE0_cause, 0))) { if (p[1] > 0) { - st->pa->cause = p[2]; + pc->para.cause = p[2]; if (p[1] > 1) - st->pa->loc = p[3]; + pc->para.loc = p[3]; else - st->pa->loc = 0; + pc->para.loc = 0; } else { - st->pa->cause = 0; - st->pa->loc = 0; + pc->para.cause = 0; + pc->para.loc = 0; } - } else - st->pa->cause = -1; - SET_SKB_FREE(skb); + } else { + pc->para.cause = -1; + l3_1tr6_error(pc, "missing REL cause", skb); + return; + } dev_kfree_skb(skb, FREE_READ); - StopAllL3Timer(st); - newl3state(st, 0); - l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + StopAllL3Timer(pc); + newl3state(pc, 0); + l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + release_l3_process(pc); } static void -l3_1tr6_rel_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - StopAllL3Timer(st); - newl3state(st, 0); - st->pa->cause = -1; - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); + StopAllL3Timer(pc); + newl3state(pc, 0); + pc->para.cause = -1; + pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); + release_l3_process(pc); } static void -l3_1tr6_disc(struct PStack *st, u_char pr, void *arg) +l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; u_char *p; int i, tmpcharge = 0; char a_charge[8], tmp[32]; - StopAllL3Timer(st); + StopAllL3Timer(pc); p = skb->data; if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) { iecpy(a_charge, p, 1); @@ -383,104 +444,110 @@ tmpcharge *= 10; tmpcharge += a_charge[i] & 0xf; } - if (tmpcharge > st->pa->chargeinfo) { - st->pa->chargeinfo = tmpcharge; - st->l3.l3l4(st, CC_INFO_CHARGE, NULL); - } - if (st->l3.debug & L3_DEB_CHARGE) { - sprintf(tmp, "charging info %d", st->pa->chargeinfo); - l3_debug(st, tmp); + if (tmpcharge > pc->para.chargeinfo) { + pc->para.chargeinfo = tmpcharge; + pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + sprintf(tmp, "charging info %d", pc->para.chargeinfo); + l3_debug(pc->st, tmp); } - } else if (st->l3.debug & L3_DEB_CHARGE) - l3_debug(st, "charging info not found"); + } else if (pc->st->l3.debug & L3_DEB_CHARGE) + l3_debug(pc->st, "charging info not found"); p = skb->data; if ((p = findie(p, skb->len, WE0_cause, 0))) { if (p[1] > 0) { - st->pa->cause = p[2]; + pc->para.cause = p[2]; if (p[1] > 1) - st->pa->loc = p[3]; + pc->para.loc = p[3]; else - st->pa->loc = 0; + pc->para.loc = 0; } else { - st->pa->cause = 0; - st->pa->loc = 0; + pc->para.cause = 0; + pc->para.loc = 0; } } else { - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "cause not found"); - st->pa->cause = -1; + if (pc->st->l3.debug & L3_DEB_WARN) + l3_debug(pc->st, "cause not found"); + pc->para.cause = -1; + } + if (!findie(skb->data, skb->len, WE6_date, 6)) { + l3_1tr6_error(pc, "missing connack date", skb); + return; } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 12); - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); + newl3state(pc, 12); + pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); } static void -l3_1tr6_connect_ack(struct PStack *st, u_char pr, void *arg) +l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); + if (!findie(skb->data, skb->len, WE6_date, 6)) { + l3_1tr6_error(pc, "missing connack date", skb); + return; + } dev_kfree_skb(skb, FREE_READ); - newl3state(st, 10); - st->pa->chargeinfo = 0; - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); + newl3state(pc, 10); + pc->para.chargeinfo = 0; + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); } static void -l3_1tr6_alert_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 7); - l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1); + newl3state(pc, 7); + l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1); } static void -l3_1tr6_setup_rsp(struct PStack *st, u_char pr, void *arg) +l3_1tr6_setup_rsp(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[24]; u_char *p = tmp; int l; - MsgHead(p, st->l3.callref, MT_N1_CONN, PROTO_DIS_N1); - if (st->pa->spv) { /* SPV ? */ + MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1); + if (pc->para.spv) { /* SPV ? */ /* NSF SPV */ *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_SPV; /* SPV */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; *p++ = WE0_netSpecFac; *p++ = 4; /* Laenge */ *p++ = 0; *p++ = FAC_Activate; /* aktiviere SPV */ - *p++ = st->pa->setup.si1; - *p++ = st->pa->setup.si2; + *p++ = pc->para.setup.si1; + *p++ = pc->para.setup.si2; } - newl3state(st, 8); + newl3state(pc, 8); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); } static void -l3_1tr6_reset(struct PStack *st, u_char pr, void *arg) +l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 0); + release_l3_process(pc); } static void -l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -489,8 +556,8 @@ u_char cause = 0x10; u_char clen = 1; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; /* Map DSS1 causes */ switch (cause & 0x7f) { case 0x10: @@ -500,57 +567,46 @@ cause = CAUSE_CallRejected; break; } - StopAllL3Timer(st); - MsgHead(p, st->l3.callref, MT_N1_DISC, PROTO_DIS_N1); + StopAllL3Timer(pc); + MsgHead(p, pc->callref, MT_N1_DISC, PROTO_DIS_N1); *p++ = WE0_cause; *p++ = clen; /* Laenge */ if (clen) *p++ = cause | 0x80; - newl3state(st, 11); + newl3state(pc, 11); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3AddTimer(&pc->timer, T305, CC_T305); } static void -l3_1tr6_release_req(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 19); - l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); -} - -static void -l3_1tr6_t303(struct PStack *st, u_char pr, void *arg) -{ - if (st->l3.n_t303 > 0) { - st->l3.n_t303--; - L3DelTimer(&st->l3.timer); - l3_1tr6_setup_req(st, pr, arg); + if (pc->N303 > 0) { + pc->N303--; + L3DelTimer(&pc->timer); + l3_1tr6_setup_req(pc, pr, arg); } else { - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); - st->l3.n_t303 = 1; - newl3state(st, 0); + L3DelTimer(&pc->timer); + pc->para.cause = 0; + l3_1tr6_disconnect_req(pc, 0, NULL); } } static void -l3_1tr6_t304(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t304(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); - + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); } static void -l3_1tr6_t305(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -559,9 +615,9 @@ u_char cause = 0x90; u_char clen = 1; - L3DelTimer(&st->l3.timer); - if (st->pa->cause > 0) - cause = st->pa->cause; + L3DelTimer(&pc->timer); + if (pc->para.cause > 0) + cause = pc->para.cause; /* Map DSS1 causes */ switch (cause & 0x7f) { case 0x10: @@ -571,74 +627,74 @@ cause = CAUSE_CallRejected; break; } - MsgHead(p, st->l3.callref, MT_N1_REL, PROTO_DIS_N1); + MsgHead(p, pc->callref, MT_N1_REL, PROTO_DIS_N1); *p++ = WE0_cause; *p++ = clen; /* Laenge */ if (clen) *p++ = cause; - newl3state(st, 19); + newl3state(pc, 19); l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3_1tr6_t310(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); } static void -l3_1tr6_t313(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3_1tr6_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_CONNECT_ERR, NULL); + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3_1tr6_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); } static void -l3_1tr6_t308_1(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t308_1(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2); - newl3state(st, 19); + L3DelTimer(&pc->timer); + l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1); + L3AddTimer(&pc->timer, T308, CC_T308_2); + newl3state(pc, 19); } static void -l3_1tr6_t308_2(struct PStack *st, u_char pr, void *arg) +l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_RELEASE_ERR, NULL); - newl3state(st, 0); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); + release_l3_process(pc); } /* *INDENT-OFF* */ static struct stateentry downstl[] = { {SBIT(0), - CC_SETUP_REQ, l3_1tr6_setup_req}, + CC_SETUP | REQUEST, l3_1tr6_setup_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10), - CC_DISCONNECT_REQ, l3_1tr6_disconnect_req}, + CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req}, {SBIT(12), - CC_RELEASE_REQ, l3_1tr6_release_req}, + CC_RELEASE | REQUEST, l3_1tr6_release_req}, {ALL_STATES, - CC_DLRL, l3_1tr6_reset}, + CC_DLRL | REQUEST, l3_1tr6_reset}, {SBIT(6), - CC_IGNORE, l3_1tr6_reset}, + CC_IGNORE | REQUEST, l3_1tr6_reset}, {SBIT(6), - CC_REJECT_REQ, l3_1tr6_disconnect_req}, + CC_REJECT | REQUEST, l3_1tr6_disconnect_req}, {SBIT(6), - CC_ALERTING_REQ, l3_1tr6_alert_req}, + CC_ALERTING | REQUEST, l3_1tr6_alert_req}, {SBIT(6) | SBIT(7), - CC_SETUP_RSP, l3_1tr6_setup_rsp}, + CC_SETUP | RESPONSE, l3_1tr6_setup_rsp}, {SBIT(1), CC_T303, l3_1tr6_t303}, {SBIT(2), @@ -655,12 +711,14 @@ CC_T308_2, l3_1tr6_t308_2}, }; -static int downstl_len = sizeof(downstl) / -sizeof(struct stateentry); +#define DOWNSTL_LEN \ + (sizeof(downstl) / sizeof(struct stateentry)) static struct stateentry datastln1[] = { {SBIT(0), + MT_N1_INVALID, l3_1tr6_invalid}, + {SBIT(0), MT_N1_SETUP, l3_1tr6_setup}, {SBIT(1), MT_N1_SETUP_ACK, l3_1tr6_setup_ack}, @@ -679,69 +737,132 @@ {SBIT(10), MT_N1_INFO, l3_1tr6_info}, {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(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17), MT_N1_REL, l3_1tr6_rel}, {SBIT(19), + MT_N1_REL, l3_1tr6_rel_ack}, + {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | + SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17), + MT_N1_REL_ACK, l3_1tr6_invalid}, + {SBIT(19), MT_N1_REL_ACK, l3_1tr6_rel_ack} }; /* *INDENT-ON* */ - - -static int datastln1_len = sizeof(datastln1) / -sizeof(struct stateentry); +#define DATASTLN1_LEN \ + (sizeof(datastln1) / sizeof(struct stateentry)) static void up1tr6(struct PStack *st, int pr, void *arg) { - int i, mt; + int i, mt, cr; + struct l3_process *proc; struct sk_buff *skb = arg; char tmp[80]; + switch (pr) { + case (DL_DATA | INDICATION): + case (DL_UNIT_DATA | INDICATION): + break; + case (DL_ESTABLISH | CONFIRM): + case (DL_ESTABLISH | INDICATION): + case (DL_RELEASE | INDICATION): + case (DL_RELEASE | CONFIRM): + l3_msg(st, pr, arg); + return; + break; + } + if (skb->len < 4) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 len only %ld", skb->len); + l3_debug(st, tmp); + } + dev_kfree_skb(skb, FREE_READ); + return; + } if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld state %d", - (pr == DL_DATA) ? " " : "(broadcast) ", - skb->data[0], skb->len, st->l3.state); + sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + skb->data[0], skb->len); + l3_debug(st, tmp); + } + dev_kfree_skb(skb, FREE_READ); + return; + } + if (skb->data[1] != 1) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 CR len not 1"); l3_debug(st, tmp); } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); return; } - mt = skb->data[skb->data[1] + 2]; + cr = skb->data[2]; + mt = skb->data[3]; if (skb->data[0] == PROTO_DIS_N0) { - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "up1tr6%s N0 state %d mt %x unhandled", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + sprintf(tmp, "up1tr6%s N0 mt %x unhandled", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt); l3_debug(st, tmp); } } else if (skb->data[0] == PROTO_DIS_N1) { - for (i = 0; i < datastln1_len; i++) + if (!(proc = getl3proc(st, cr))) { + if (mt == MT_N1_SETUP) { + if (cr < 128) { + if (!(proc = new_l3_process(st, cr))) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 no roc mem"); + l3_debug(st, tmp); + } + dev_kfree_skb(skb, FREE_READ); + return; + } + } else { + dev_kfree_skb(skb, FREE_READ); + return; + } + } else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) || + (mt == MT_N1_CANC_ACK) || (mt == MT_N1_CANC_REJ) || + (mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) || + (mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) || + (mt == MT_N1_INFO)) { + dev_kfree_skb(skb, FREE_READ); + return; + } else { + if (!(proc = new_l3_process(st, cr))) { + if (st->l3.debug & L3_DEB_PROTERR) { + sprintf(tmp, "up1tr6 no roc mem"); + l3_debug(st, tmp); + } + dev_kfree_skb(skb, FREE_READ); + return; + } + mt = MT_N1_INVALID; + } + } + for (i = 0; i < DATASTLN1_LEN; i++) if ((mt == datastln1[i].primitive) && - ((1 << st->l3.state) & datastln1[i].state)) + ((1 << proc->state) & datastln1[i].state)) break; - if (i == datastln1_len) { - SET_SKB_FREE(skb); + if (i == DATASTLN1_LEN) { dev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x unhandled", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); l3_debug(st, tmp); } return; } else { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "up1tr6%sstate %d mt %x", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); l3_debug(st, tmp); } - datastln1[i].rout(st, pr, skb); + datastln1[i].rout(proc, pr, skb); } } } @@ -749,26 +870,47 @@ static void down1tr6(struct PStack *st, int pr, void *arg) { - int i; + int i, cr; + struct l3_process *proc; + struct Channel *chan; char tmp[80]; - for (i = 0; i < downstl_len; i++) + if (((DL_ESTABLISH | REQUEST)== pr) || ((DL_RELEASE | REQUEST)== pr)) { + l3_msg(st, pr, NULL); + return; + } else if ((CC_SETUP | REQUEST) == pr) { + chan = arg; + cr = newcallref(); + cr |= 0x80; + if (!(proc = new_l3_process(st, cr))) { + return; + } else { + proc->chan = chan; + chan->proc = proc; + proc->para.setup = chan->setup; + proc->callref = cr; + } + } else { + proc = arg; + } + + for (i = 0; i < DOWNSTL_LEN; i++) if ((pr == downstl[i].primitive) && - ((1 << st->l3.state) & downstl[i].state)) + ((1 << proc->state) & downstl[i].state)) break; - if (i == downstl_len) { + if (i == DOWNSTL_LEN) { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "down1tr6 state %d prim %d unhandled", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } } else { if (st->l3.debug & L3_DEB_STATE) { sprintf(tmp, "down1tr6 state %d prim %d", - st->l3.state, pr); + proc->state, pr); l3_debug(st, tmp); } - downstl[i].rout(st, pr, arg); + downstl[i].rout(proc, pr, arg); } } @@ -777,20 +919,10 @@ { char tmp[64]; - st->l4.l4l3 = down1tr6; + st->lli.l4l3 = down1tr6; st->l2.l2l3 = up1tr6; - st->l3.t303 = 4000; - st->l3.t304 = 20000; - st->l3.t305 = 4000; - st->l3.t308 = 4000; - st->l3.t310 = 120000; - st->l3.t313 = 4000; - st->l3.t318 = 4000; - st->l3.t319 = 4000; - st->l3.n_t303 = 0; - - if (st->l3.channr & 1) { - strcpy(tmp, l3_1tr6_revision); - printk(KERN_NOTICE "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp)); - } + st->l3.N303 = 0; + + strcpy(tmp, l3_1tr6_revision); + printk(KERN_INFO "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp)); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/l3_1tr6.h linux/drivers/isdn/hisax/l3_1tr6.h --- v2.0.35/linux/drivers/isdn/hisax/l3_1tr6.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/l3_1tr6.h Sun Nov 15 10:32:58 1998 @@ -1,12 +1,19 @@ -/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil Exp $ +/* $Id: l3_1tr6.h,v 1.1.2.2 1998/09/27 13:06:46 keil Exp $ * * German 1TR6 D-channel protocol defines * * $Log: l3_1tr6.h,v $ - * Revision 1.1 1996/10/13 20:03:48 keil - * Initial revision + * Revision 1.1.2.2 1998/09/27 13:06:46 keil + * Apply most changes from 2.1.X (HiSax 3.1) * + * Revision 1.1.2.1 1997/10/17 22:14:15 keil + * update to last hisax version * + * Revision 2.0 1997/07/27 21:15:47 keil + * New Callref based layer3 + * + * Revision 1.1 1996/10/13 20:03:48 keil + * Initial revision * */ #ifndef l3_1tr6 @@ -29,7 +36,6 @@ #define MT_N0_CLOSE 0x75 #define MT_N0_CLO_ACK 0x77 - /* * MsgType N1 */ @@ -64,8 +70,7 @@ #define MT_N1_REG_ACK 0x6C #define MT_N1_REG_REJ 0x6F #define MT_N1_STAT 0x63 - - +#define MT_N1_INVALID 0 /* * W Elemente @@ -156,5 +161,13 @@ #define CAUSE_RemoteUserResumed 0x73 #define CAUSE_UserInfoDiscarded 0x7F +#define T303 4000 +#define T304 20000 +#define T305 4000 +#define T308 4000 +#define T310 120000 +#define T313 4000 +#define T318 4000 +#define T319 4000 #endif diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c --- v2.0.35/linux/drivers/isdn/hisax/l3dss1.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/l3dss1.c Sun Nov 15 10:32:58 1998 @@ -1,82 +1,303 @@ -/* $Id: l3dss1.c,v 1.16 1997/06/03 20:43:46 keil Exp $ +/* $Id: l3dss1.c,v 1.16.2.8 1998/11/03 00:07:14 keil Exp $ * EURO/DSS1 D-channel protocol * * Author Karsten Keil (keil@temic-ech.spacenet.de) * based on the teles driver from Jan den Ouden * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * * Thanks to Jan den Ouden * Fritz Elfert * * $Log: l3dss1.c,v $ - * Revision 1.16 1997/06/03 20:43:46 keil - * Display numbers as default - * - * Revision 1.15 1997/04/17 11:50:48 keil - * pa->loc was undefined, if it was not send by the exchange - * - * Revision 1.14 1997/04/06 22:54:20 keil - * Using SKB's - * - * Revision 1.13 1997/03/13 20:37:28 keil - * CLIR and channel request added - * - * Revision 1.12 1997/02/17 00:34:26 keil - * Bugfix: Wrong cause delivered + * Revision 1.16.2.8 1998/11/03 00:07:14 keil + * certification related changes + * fixed logging for smaller stack use * - * Revision 1.11 1997/02/16 12:12:47 fritz - * Bugfix: SI2 was nont initialized on incoming calls. + * Revision 1.16.2.7 1998/10/25 18:16:25 fritz + * Replaced some read-only variables by defines. * - * Revision 1.10 1997/02/11 01:37:24 keil - * Changed setup-interface (incoming and outgoing) + * Revision 1.16.2.6 1998/10/23 15:00:56 fritz + * Eliminated a compiler warning. * - * Revision 1.9 1997/01/27 23:20:52 keil - * report revision only ones + * Revision 1.16.2.5 1998/09/27 13:06:48 keil + * Apply most changes from 2.1.X (HiSax 3.1) * - * Revision 1.8 1997/01/21 22:29:41 keil - * new statemachine; L3 timers + * Revision 1.16.2.4 1998/05/27 18:06:08 keil + * HiSax 3.0 * - * Revision 1.7 1996/12/14 21:06:59 keil - * additional states for CC_REJECT + * Revision 1.16.2.3 1998/02/03 23:16:06 keil + * german AOC * - * Revision 1.6 1996/12/08 22:59:16 keil - * fixed calling party number without octet 3a + * Revision 1.16.2.2 1997/11/15 18:54:15 keil + * cosmetics * - * Revision 1.5 1996/12/08 19:53:31 keil - * fixes from Pekka Sarnila + * Revision 1.16.2.1 1997/10/17 22:14:16 keil + * update to last hisax version * - * Revision 1.4 1996/11/05 19:44:36 keil - * some fixes from Henner Eisen + * Revision 2.2 1997/08/07 17:44:36 keil + * Fix RESTART * - * Revision 1.3 1996/10/30 10:18:01 keil - * bugfixes in debugging output + * Revision 2.1 1997/08/03 14:36:33 keil + * Implement RESTART procedure * - * Revision 1.2 1996/10/27 22:15:16 keil - * bugfix reject handling + * Revision 2.0 1997/07/27 21:15:43 keil + * New Callref based layer3 * - * Revision 1.1 1996/10/13 20:04:55 keil - * Initial revision + * Revision 1.17 1997/06/26 11:11:46 keil + * SET_SKBFREE now on creation of a SKB * + * Revision 1.15 1997/04/17 11:50:48 keil + * pa->loc was undefined, if it was not send by the exchange * + * Old log removed /KKe * */ #define __NO_VERSION__ #include "hisax.h" #include "isdnl3.h" +#include "l3dss1.h" #include extern char *HiSax_getrev(const char *revision); -const char *dss1_revision = "$Revision: 1.16 $"; +const char *dss1_revision = "$Revision: 1.16.2.8 $"; + +#define EXT_BEARER_CAPS 1 #define MsgHead(ptr, cref, mty) \ *ptr++ = 0x8; \ *ptr++ = 0x1; \ - *ptr++ = cref; \ + *ptr++ = cref^0x80; \ *ptr++ = mty + +#if HISAX_DE_AOC static void -l3dss1_message(struct PStack *st, u_char mt) +l3dss1_parse_facility(struct l3_process *pc, u_char * p) +{ + int qd_len = 0; + + p++; + qd_len = *p++; + if (qd_len == 0) { + l3_debug(pc->st, "qd_len == 0"); + return; + } + if ((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */ + l3_debug(pc->st, "supplementary service != 0x11"); + return; + } + while (qd_len > 0 && !(*p & 0x80)) { /* extension ? */ + p++; + qd_len--; + } + if (qd_len < 2) { + l3_debug(pc->st, "qd_len < 2"); + return; + } + p++; + qd_len--; + if ((*p & 0xE0) != 0xA0) { /* class and form */ + l3_debug(pc->st, "class and form != 0xA0"); + return; + } + switch (*p & 0x1F) { /* component tag */ + case 1: /* invoke */ + { + unsigned char nlen = 0, ilen; + int ident; + + p++; + qd_len--; + if (qd_len < 1) { + l3_debug(pc->st, "qd_len < 1"); + break; + } + if (*p & 0x80) { /* length format */ + l3_debug(pc->st, "*p & 0x80 length format"); + break; + } + nlen = *p++; + qd_len--; + if (qd_len < nlen) { + l3_debug(pc->st, "qd_len < nlen"); + return; + } + qd_len -= nlen; + + if (nlen < 2) { + l3_debug(pc->st, "nlen < 2"); + return; + } + if (*p != 0x02) { /* invoke identifier tag */ + l3_debug(pc->st, "invoke identifier tag !=0x02"); + return; + } + p++; + nlen--; + if (*p & 0x80) { /* length format */ + l3_debug(pc->st, "*p & 0x80 length format 2"); + break; + } + ilen = *p++; + nlen--; + if (ilen > nlen || ilen == 0) { + l3_debug(pc->st, "ilen > nlen || ilen == 0"); + return; + } + nlen -= ilen; + ident = 0; + while (ilen > 0) { + ident = (ident << 8) | (*p++ & 0xFF); /* invoke identifier */ + ilen--; + } + + if (nlen < 2) { + l3_debug(pc->st, "nlen < 2 22"); + return; + } + if (*p != 0x02) { /* operation value */ + l3_debug(pc->st, "operation value !=0x02"); + return; + } + p++; + nlen--; + ilen = *p++; + nlen--; + if (ilen > nlen || ilen == 0) { + l3_debug(pc->st, "ilen > nlen || ilen == 0 22"); + return; + } + nlen -= ilen; + ident = 0; + while (ilen > 0) { + ident = (ident << 8) | (*p++ & 0xFF); + ilen--; + } + +#define FOO1(s,a,b) \ + while(nlen > 1) { \ + int ilen = p[1]; \ + if(nlen < ilen+2) { \ + l3_debug(pc->st, "FOO1 nlen < ilen+2"); \ + return; \ + } \ + nlen -= ilen+2; \ + if((*p & 0xFF) == (a)) { \ + int nlen = ilen; \ + p += 2; \ + b; \ + } else { \ + p += ilen+2; \ + } \ + } + + switch (ident) { + default: + break; + case 0x22: /* during */ + FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( { + ident = 0; + nlen = (nlen)?nlen:0; /* Make gcc happy */ + while (ilen > 0) { + ident = (ident << 8) | *p++; + ilen--; + } + if (ident > pc->para.chargeinfo) { + pc->para.chargeinfo = ident; + pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + if (*(p + 2) == 0) { + l3_debug(pc->st, "charging info during %d", pc->para.chargeinfo); + } + else { + l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo); + } + } + } + ))))) + break; + case 0x24: /* final */ + FOO1("2A", 0x30, FOO1("2B", 0x30, FOO1("2C", 0xA1, FOO1("2D", 0x30, FOO1("2E", 0x02, ( { + ident = 0; + nlen = (nlen)?nlen:0; /* Make gcc happy */ + while (ilen > 0) { + ident = (ident << 8) | *p++; + ilen--; + } + if (ident > pc->para.chargeinfo) { + pc->para.chargeinfo = ident; + pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc); + } + if (pc->st->l3.debug & L3_DEB_CHARGE) { + l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo); + } + } + )))))) + break; + } +#undef FOO1 + + } + break; + case 2: /* return result */ + l3_debug(pc->st, "return result break"); + break; + case 3: /* return error */ + l3_debug(pc->st, "return error break"); + break; + default: + l3_debug(pc->st, "default break"); + break; + } +} +#endif + +static int +l3dss1_check_messagetype_validity(int mt) +{ +/* verify if a message type exists */ + switch (mt) { + case MT_ALERTING: + case MT_CALL_PROCEEDING: + case MT_CONNECT: + case MT_CONNECT_ACKNOWLEDGE: + case MT_PROGRESS: + case MT_SETUP: + case MT_SETUP_ACKNOWLEDGE: + case MT_RESUME: + case MT_RESUME_ACKNOWLEDGE: + case MT_RESUME_REJECT: + case MT_SUSPEND: + case MT_SUSPEND_ACKNOWLEDGE: + case MT_SUSPEND_REJECT: + case MT_USER_INFORMATION: + case MT_DISCONNECT: + case MT_RELEASE: + case MT_RELEASE_COMPLETE: + case MT_RESTART: + case MT_RESTART_ACKNOWLEDGE: + case MT_SEGMENT: + case MT_CONGESTION_CONTROL: + case MT_INFORMATION: + case MT_FACILITY: + case MT_NOTIFY: + case MT_STATUS: + case MT_STATUS_ENQUIRY: + return(1); + default: + return(0); + } + return(0); +} + +static void +l3dss1_message(struct l3_process *pc, u_char mt) { struct sk_buff *skb; u_char *p; @@ -84,44 +305,283 @@ if (!(skb = l3_alloc_skb(4))) return; p = skb_put(skb, 4); - MsgHead(p, st->l3.callref, mt); - st->l3.l3l2(st, DL_DATA, skb); + MsgHead(p, pc->callref, mt); + l3_msg(pc->st, DL_DATA | REQUEST, skb); } static void -l3dss1_release_req(struct PStack *st, u_char pr, void *arg) +l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 19); - l3dss1_message(st, MT_RELEASE); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + StopAllL3Timer(pc); + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); } static void -l3dss1_release_cmpl(struct PStack *st, u_char pr, void *arg) +l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int cause = -1; p = skb->data; - st->pa->loc = 0; + pc->para.loc = 0; if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - StopAllL3Timer(st); - st->pa->cause = cause; - newl3state(st, 0); - st->l3.l3l4(st, CC_RELEASE_CNF, NULL); + StopAllL3Timer(pc); + pc->para.cause = cause; + newl3state(pc, 0); + pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc); + release_l3_process(pc); +} + +#if EXT_BEARER_CAPS + +u_char * +EncodeASyncParams(u_char * p, u_char si2) +{ // 7c 06 88 90 21 42 00 bb + + p[0] = p[1] = 0; + p[2] = 0x80; + if (si2 & 32) // 7 data bits + + p[2] += 16; + else // 8 data bits + + p[2] += 24; + + if (si2 & 16) // 2 stop bits + + p[2] += 96; + else // 1 stop bit + + p[2] = 32; + + if (si2 & 8) // even parity + + p[2] += 2; + else // no parity + + p[2] += 3; + + switch (si2 & 0x07) { + case 0: + p[0] = 66; // 1200 bit/s + + break; + case 1: + p[0] = 88; // 1200/75 bit/s + + break; + case 2: + p[0] = 87; // 75/1200 bit/s + + break; + case 3: + p[0] = 67; // 2400 bit/s + + break; + case 4: + p[0] = 69; // 4800 bit/s + + break; + case 5: + p[0] = 72; // 9600 bit/s + + break; + case 6: + p[0] = 73; // 14400 bit/s + + break; + case 7: + p[0] = 75; // 19200 bit/s + + break; + } + return p + 3; +} + +u_char +EncodeSyncParams(u_char si2, u_char ai) +{ + + switch (si2) { + case 0: + return ai + 2; // 1200 bit/s + + case 1: + return ai + 24; // 1200/75 bit/s + + case 2: + return ai + 23; // 75/1200 bit/s + + case 3: + return ai + 3; // 2400 bit/s + + case 4: + return ai + 5; // 4800 bit/s + + case 5: + return ai + 8; // 9600 bit/s + + case 6: + return ai + 9; // 14400 bit/s + + case 7: + return ai + 11; // 19200 bit/s + + case 8: + return ai + 14; // 48000 bit/s + + case 9: + return ai + 15; // 56000 bit/s + + case 15: + return ai + 40; // negotiate bit/s + + default: + break; + } + return ai; +} + + +static u_char +DecodeASyncParams(u_char si2, u_char * p) +{ + u_char info; + + switch (p[5]) { + case 66: // 1200 bit/s + + break; // si2 don't change + + case 88: // 1200/75 bit/s + + si2 += 1; + break; + case 87: // 75/1200 bit/s + + si2 += 2; + break; + case 67: // 2400 bit/s + + si2 += 3; + break; + case 69: // 4800 bit/s + + si2 += 4; + break; + case 72: // 9600 bit/s + + si2 += 5; + break; + case 73: // 14400 bit/s + + si2 += 6; + break; + case 75: // 19200 bit/s + + si2 += 7; + break; + } + + info = p[7] & 0x7f; + if ((info & 16) && (!(info & 8))) // 7 data bits + + si2 += 32; // else 8 data bits + + if ((info & 96) == 96) // 2 stop bits + + si2 += 16; // else 1 stop bit + + if ((info & 2) && (!(info & 1))) // even parity + + si2 += 8; // else no parity + + return si2; } + +static u_char +DecodeSyncParams(u_char si2, u_char info) +{ + info &= 0x7f; + switch (info) { + case 40: // bit/s negotiation failed ai := 165 not 175! + + return si2 + 15; + case 15: // 56000 bit/s failed, ai := 0 not 169 ! + + return si2 + 9; + case 14: // 48000 bit/s + + return si2 + 8; + case 11: // 19200 bit/s + + return si2 + 7; + case 9: // 14400 bit/s + + return si2 + 6; + case 8: // 9600 bit/s + + return si2 + 5; + case 5: // 4800 bit/s + + return si2 + 4; + case 3: // 2400 bit/s + + return si2 + 3; + case 23: // 75/1200 bit/s + + return si2 + 2; + case 24: // 1200/75 bit/s + + return si2 + 1; + default: // 1200 bit/s + + return si2; + } +} + +static u_char +DecodeSI2(struct sk_buff *skb) +{ + u_char *p; //, *pend=skb->data + skb->len; + + if ((p = findie(skb->data, skb->len, 0x7c, 0))) { + switch (p[4] & 0x0f) { + case 0x01: + if (p[1] == 0x04) // sync. Bitratenadaption + + return DecodeSyncParams(160, p[5]); // V.110/X.30 + + else if (p[1] == 0x06) // async. Bitratenadaption + + return DecodeASyncParams(192, p); // V.110/X.30 + + break; + case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption + + return DecodeSyncParams(176, p[5]); // V.120 + + break; + } + } + return 0; +} + +#endif + + static void -l3dss1_setup_req(struct PStack *st, u_char pr, +l3dss1_setup_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; @@ -131,16 +591,19 @@ u_char screen = 0x80; u_char *teln; u_char *msn; + u_char *sub; + u_char *sp; int l; - st->l3.callref = st->pa->callref; - MsgHead(p, st->l3.callref, MT_SETUP); + MsgHead(p, pc->callref, MT_SETUP); /* * Set Bearer Capability, Map info from 1TR6-convention to EDSS1 */ +#if HISAX_EURO_SENDCOMPLETE *p++ = 0xa1; /* complete indicator */ - switch (st->pa->setup.si1) { +#endif + switch (pc->para.setup.si1) { case 1: /* Telephony */ *p++ = 0x4; /* BC-IE-code */ *p++ = 0x3; /* Length */ @@ -160,7 +623,7 @@ /* * What about info2? Mapping to High-Layer-Compatibility? */ - teln = st->pa->setup.phone; + teln = pc->para.setup.phone; if (*teln) { /* parse number for special things */ if (!isdigit(*teln)) { @@ -182,19 +645,28 @@ screen = 0x80; break; default: - if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "Wrong MSN Code"); + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "Wrong MSN Code"); break; } teln++; } } if (channel) { - *p++ = 0x18; /* channel indicator */ + *p++ = IE_CHANNEL_ID; *p++ = 1; *p++ = channel; } - msn = st->pa->setup.eazmsn; + msn = pc->para.setup.eazmsn; + sub = NULL; + sp = msn; + while (*sp) { + if ('.' == *sp) { + sub = sp; + *sp = 0; + } else + sp++; + } if (*msn) { *p++ = 0x6c; *p++ = strlen(msn) + (screen ? 2 : 1); @@ -207,241 +679,389 @@ while (*msn) *p++ = *msn++ & 0x7f; } + if (sub) { + *sub++ = '.'; + *p++ = 0x6d; /* Calling party subaddress */ + *p++ = strlen(sub) + 2; + *p++ = 0x80; /* NSAP coded */ + *p++ = 0x50; /* local IDI format */ + while (*sub) + *p++ = *sub++ & 0x7f; + } + sub = NULL; + sp = teln; + while (*sp) { + if ('.' == *sp) { + sub = sp; + *sp = 0; + } else + sp++; + } *p++ = 0x70; *p++ = strlen(teln) + 1; /* Classify as AnyPref. */ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */ - while (*teln) *p++ = *teln++ & 0x7f; + if (sub) { + *sub++ = '.'; + *p++ = 0x71; /* Called party subaddress */ + *p++ = strlen(sub) + 2; + *p++ = 0x80; /* NSAP coded */ + *p++ = 0x50; /* local IDI format */ + while (*sub) + *p++ = *sub++ & 0x7f; + } +#if EXT_BEARER_CAPS + if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30 + + *p++ = 0x7c; + *p++ = 0x04; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x21; + *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80); + } else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) { // sync. Bitratenadaption, V.120 + + *p++ = 0x7c; + *p++ = 0x05; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x28; + *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0); + *p++ = 0x82; + } else if (pc->para.setup.si2 >= 192) { // async. Bitratenadaption, V.110/X.30 + + *p++ = 0x7c; + *p++ = 0x06; + *p++ = 0x88; + *p++ = 0x90; + *p++ = 0x21; + p = EncodeASyncParams(p, pc->para.setup.si2 - 192); +#if HISAX_SEND_STD_LLC_IE + } else { + *p++ = 0x7c; + *p++ = 0x02; + *p++ = 0x88; + *p++ = 0x90; +#endif + } +#endif l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303); - newl3state(st, 1); - st->l3.l3l2(st, DL_DATA, skb); - + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T303, CC_T303); + newl3state(pc, 1); + l3_msg(pc->st, DL_DATA | REQUEST, skb); } static void -l3dss1_call_proc(struct PStack *st, u_char pr, void *arg) +l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN)) - l3_debug(st, "setup answer without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "setup answer without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 3); - L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310); - st->l3.l3l4(st, CC_PROCEEDING_IND, NULL); + newl3state(pc, 3); + L3AddTimer(&pc->timer, T310, CC_T310); + pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc); } static void -l3dss1_setup_ack(struct PStack *st, u_char pr, void *arg) +l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; - L3DelTimer(&st->l3.timer); + L3DelTimer(&pc->timer); p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if ((!st->pa->bchannel) && (st->l3.debug & L3_DEB_WARN)) - l3_debug(st, "setup answer without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup answer without bchannel"); - SET_SKB_FREE(skb); + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "setup answer without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup answer without bchannel"); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 2); - L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304); - st->l3.l3l4(st, CC_MORE_INFO, NULL); + newl3state(pc, 2); + L3AddTimer(&pc->timer, T304, CC_T304); + pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc); } static void -l3dss1_disconnect(struct PStack *st, u_char pr, void *arg) +l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; int cause = -1; - StopAllL3Timer(st); + StopAllL3Timer(pc); p = skb->data; - st->pa->loc = 0; + pc->para.loc = 0; if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 12); - st->pa->cause = cause; - st->l3.l3l4(st, CC_DISCONNECT_IND, NULL); + newl3state(pc, 12); + pc->para.cause = cause; + pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc); } static void -l3dss1_connect(struct PStack *st, u_char pr, void *arg) +l3dss1_connect(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - L3DelTimer(&st->l3.timer); /* T310 */ - newl3state(st, 10); - st->l3.l3l4(st, CC_SETUP_CNF, NULL); + L3DelTimer(&pc->timer); /* T310 */ + newl3state(pc, 10); + pc->para.chargeinfo = 0; + pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc); } static void -l3dss1_alerting(struct PStack *st, u_char pr, void *arg) +l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - L3DelTimer(&st->l3.timer); /* T304 */ - newl3state(st, 4); - st->l3.l3l4(st, CC_ALERTING_IND, NULL); + L3DelTimer(&pc->timer); /* T304 */ + newl3state(pc, 4); + pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc); } static void -l3dss1_setup(struct PStack *st, u_char pr, void *arg) +l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg) { - u_char *p; + /* This routine is called if here was no SETUP made (checks in dss1up and in + * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code + * MT_STATUS_ENQUIRE in the NULL state is handled too + */ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb; + + switch (pc->para.cause) { + case 81: /* 0x51 invalid callreference */ + case 88: /* 0x58 incomp destination */ + case 96: /* 0x60 mandory IE missing */ + case 101: /* 0x65 incompatible Callstate */ + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = pc->para.cause | 0x80; + break; + default: + printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n"); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + release_l3_process(pc); +} + +static void +l3dss1_setup(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p, *ptmp[8]; + int i; int bcfound = 0; char tmp[80]; struct sk_buff *skb = arg; + /* ETS 300-104 1.3.4 and 1.3.5 + * we need to detect unknown inform. element from 0 to 7 + */ p = skb->data; - st->pa->callref = getcallref(p); - st->l3.callref = 0x80 + st->pa->callref; - + for (i = 0; i < 8; i++) + ptmp[i] = skb->data; + if (findie(ptmp[1], skb->len, 0x01, 0) + || findie(ptmp[2], skb->len, 0x02, 0) + || findie(ptmp[3], skb->len, 0x03, 0) + || findie(ptmp[5], skb->len, 0x05, 0) + || findie(ptmp[6], skb->len, 0x06, 0) + || findie(ptmp[7], skb->len, 0x07, 0)) { + /* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE + * cause 0x60 + */ + pc->para.cause = 0x60; + dev_kfree_skb(skb, FREE_READ); + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } /* * Channel Identification */ p = skb->data; - if ((p = findie(p, skb->len, 0x18, 0))) { - st->pa->bchannel = p[2] & 0x3; - if (st->pa->bchannel) + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if (pc->para.bchannel) bcfound++; - else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bchannel"); - + else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bchannel"); + pc->para.cause = 0x60; + dev_kfree_skb(skb, FREE_READ); + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } /* * Bearer Capabilities */ p = skb->data; if ((p = findie(p, skb->len, 0x04, 0))) { - st->pa->setup.si2 = 0; + pc->para.setup.si2 = 0; switch (p[2] & 0x1f) { case 0x00: /* Speech */ case 0x10: /* 3.1 Khz audio */ - st->pa->setup.si1 = 1; + pc->para.setup.si1 = 1; break; case 0x08: /* Unrestricted digital information */ - st->pa->setup.si1 = 7; + pc->para.setup.si1 = 7; +/* JIM, 05.11.97 I wanna set service indicator 2 */ +#if EXT_BEARER_CAPS + pc->para.setup.si2 = DecodeSI2(skb); + printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n", + pc->para.setup.si1, pc->para.setup.si2); +#endif break; case 0x09: /* Restricted digital information */ - st->pa->setup.si1 = 2; + pc->para.setup.si1 = 2; break; case 0x11: /* Unrestr. digital information with tones/announcements */ - st->pa->setup.si1 = 3; + pc->para.setup.si1 = 3; break; case 0x18: /* Video */ - st->pa->setup.si1 = 4; + pc->para.setup.si1 = 4; break; default: - st->pa->setup.si1 = 0; + pc->para.setup.si1 = 0; } - } else if (st->l3.debug & L3_DEB_WARN) - l3_debug(st, "setup without bearer capabilities"); + } else { + if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "setup without bearer capabilities"); + /* ETS 300-104 1.3.3 */ + pc->para.cause = 0x60; + dev_kfree_skb(skb, FREE_READ); + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } p = skb->data; if ((p = findie(p, skb->len, 0x70, 0))) - iecpy(st->pa->setup.eazmsn, p, 1); + iecpy(pc->para.setup.eazmsn, p, 1); else - st->pa->setup.eazmsn[0] = 0; + pc->para.setup.eazmsn[0] = 0; p = skb->data; + if ((p = findie(p, skb->len, 0x71, 0))) { + /* Called party subaddress */ + if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) { + tmp[0] = '.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.eazmsn, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong called subaddress"); + } + p = skb->data; if ((p = findie(p, skb->len, 0x6c, 0))) { - st->pa->setup.plan = p[2]; + pc->para.setup.plan = p[2]; if (p[2] & 0x80) { - iecpy(st->pa->setup.phone, p, 1); - st->pa->setup.screen = 0; + iecpy(pc->para.setup.phone, p, 1); + pc->para.setup.screen = 0; } else { - iecpy(st->pa->setup.phone, p, 2); - st->pa->setup.screen = p[3]; + iecpy(pc->para.setup.phone, p, 2); + pc->para.setup.screen = p[3]; } } else { - st->pa->setup.phone[0] = 0; - st->pa->setup.plan = 0; - st->pa->setup.screen = 0; + pc->para.setup.phone[0] = 0; + pc->para.setup.plan = 0; + pc->para.setup.screen = 0; + } + p = skb->data; + if ((p = findie(p, skb->len, 0x6d, 0))) { + /* Calling party subaddress */ + if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) { + tmp[0] = '.'; + iecpy(&tmp[1], p, 2); + strcat(pc->para.setup.phone, tmp); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "wrong calling subaddress"); } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); if (bcfound) { - if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) { - sprintf(tmp, "non-digital call: %s -> %s", - st->pa->setup.phone, - st->pa->setup.eazmsn); - l3_debug(st, tmp); + if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) { + l3_debug(pc->st, "non-digital call: %s -> %s", + pc->para.setup.phone, pc->para.setup.eazmsn); } - newl3state(st, 6); - st->l3.l3l4(st, CC_SETUP_IND, NULL); - } + if ((pc->para.setup.si1 != 7) && + test_bit(FLG_PTP, &pc->st->l2.flag)) { + pc->para.cause = 0x58; + l3dss1_msg_without_setup(pc, pr, NULL); + return; + } + newl3state(pc, 6); + pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc); + } else + release_l3_process(pc); } static void -l3dss1_reset(struct PStack *st, u_char pr, void *arg) +l3dss1_reset(struct l3_process *pc, u_char pr, void *arg) { - StopAllL3Timer(st); - newl3state(st, 0); + release_l3_process(pc); } static void -l3dss1_setup_rsp(struct PStack *st, u_char pr, +l3dss1_setup_rsp(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 8); - l3dss1_message(st, MT_CONNECT); - L3DelTimer(&st->l3.timer); - L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313); + newl3state(pc, 8); + l3dss1_message(pc, MT_CONNECT); + L3DelTimer(&pc->timer); + L3AddTimer(&pc->timer, T313, CC_T313); } static void -l3dss1_connect_ack(struct PStack *st, u_char pr, void *arg) +l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - newl3state(st, 10); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL); + newl3state(pc, 10); + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc); } static void -l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg) +l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -449,12 +1069,12 @@ int l; u_char cause = 0x10; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; - StopAllL3Timer(st); + StopAllL3Timer(pc); - MsgHead(p, st->l3.callref, MT_DISCONNECT); + MsgHead(p, pc->callref, MT_DISCONNECT); *p++ = IE_CAUSE; *p++ = 0x2; @@ -465,13 +1085,13 @@ if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - newl3state(st, 11); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305); + newl3state(pc, 11); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3AddTimer(&pc->timer, T305, CC_T305); } static void -l3dss1_reject_req(struct PStack *st, u_char pr, void *arg) +l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg) { struct sk_buff *skb; u_char tmp[16]; @@ -479,10 +1099,10 @@ int l; u_char cause = 0x95; - if (st->pa->cause > 0) - cause = st->pa->cause; + if (pc->para.cause > 0) + cause = pc->para.cause; - MsgHead(p, st->l3.callref, MT_RELEASE_COMPLETE); + MsgHead(p, pc->callref, MT_RELEASE_COMPLETE); *p++ = IE_CAUSE; *p++ = 0x2; @@ -493,13 +1113,14 @@ if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - newl3state(st, 0); - st->l3.l3l2(st, DL_DATA, skb); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + release_l3_process(pc); } static void -l3dss1_release(struct PStack *st, u_char pr, void *arg) +l3dss1_release(struct l3_process *pc, u_char pr, void *arg) { u_char *p; struct sk_buff *skb = arg; @@ -509,38 +1130,45 @@ if ((p = findie(p, skb->len, IE_CAUSE, 0))) { p++; if (*p++ == 2) - st->pa->loc = *p++; + pc->para.loc = *p++; cause = *p & 0x7f; } - SET_SKB_FREE(skb); + p = skb->data; + if ((p = findie(p, skb->len, IE_FACILITY, 0))) { +#if HISAX_DE_AOC + l3dss1_parse_facility(pc, p); +#else + p = NULL; +#endif + } dev_kfree_skb(skb, FREE_READ); - StopAllL3Timer(st); - st->pa->cause = cause; - newl3state(st, 0); - l3dss1_message(st, MT_RELEASE_COMPLETE); - st->l3.l3l4(st, CC_RELEASE_IND, NULL); + StopAllL3Timer(pc); + pc->para.cause = cause; + l3dss1_message(pc, MT_RELEASE_COMPLETE); + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + release_l3_process(pc); } static void -l3dss1_alert_req(struct PStack *st, u_char pr, +l3dss1_alert_req(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 7); - l3dss1_message(st, MT_ALERTING); + newl3state(pc, 7); + l3dss1_message(pc, MT_ALERTING); } static void -l3dss1_status_enq(struct PStack *st, u_char pr, void *arg) +l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg) { u_char tmp[16]; u_char *p = tmp; int l; struct sk_buff *skb = arg; - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); - MsgHead(p, st->l3.callref, MT_STATUS); + MsgHead(p, pc->callref, MT_STATUS); *p++ = IE_CAUSE; *p++ = 0x2; @@ -549,42 +1177,96 @@ *p++ = 0x14; /* CallState */ *p++ = 0x1; - *p++ = st->l3.state & 0x3f; + *p++ = pc->state & 0x3f; + + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); +} + +static void +l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg) +{ + /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1... + if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62 */ + u_char tmp[16]; + u_char *p = tmp; + int l; + struct sk_buff *skb = arg; + + dev_kfree_skb(skb, FREE_READ); + + MsgHead(p, pc->callref, MT_STATUS); + + *p++ = IE_CAUSE; + *p++ = 0x2; + *p++ = 0x80; + *p++ = 0x62 | 0x80; /* status sending */ + + *p++ = 0x14; /* CallState */ + *p++ = 0x1; + *p++ = pc->state & 0x3f; l = p - tmp; if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - st->l3.l3l2(st, DL_DATA, skb); + l3_msg(pc->st, DL_DATA | REQUEST, skb); } static void -l3dss1_t303(struct PStack *st, u_char pr, void *arg) +l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg) { - if (st->l3.n_t303 > 0) { - st->l3.n_t303--; - L3DelTimer(&st->l3.timer); - l3dss1_setup_req(st, pr, arg); + u_char *p; + struct sk_buff *skb = arg; + int callState = 0; + p = skb->data; + + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1 == *p++) + callState = *p; + } + if (callState == 0) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1 + * set down layer 3 without sending any message + */ + pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc); + newl3state(pc, 0); + release_l3_process(pc); } else { - newl3state(st, 0); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL); - st->l3.n_t303 = 1; + pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc); } } static void -l3dss1_t304(struct PStack *st, u_char pr, void *arg) +l3dss1_t303(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + if (pc->N303 > 0) { + pc->N303--; + L3DelTimer(&pc->timer); + l3dss1_setup_req(pc, pr, arg); + } else { + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc); + release_l3_process(pc); + } +} + +static void +l3dss1_t304(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); } static void -l3dss1_t305(struct PStack *st, u_char pr, void *arg) +l3dss1_t305(struct l3_process *pc, u_char pr, void *arg) { u_char tmp[16]; u_char *p = tmp; @@ -592,11 +1274,11 @@ struct sk_buff *skb; u_char cause = 0x90; - L3DelTimer(&st->l3.timer); - if (st->pa->cause > 0) - cause = st->pa->cause; + L3DelTimer(&pc->timer); + if (pc->para.cause > 0) + cause = pc->para.cause | 0x80; - MsgHead(p, st->l3.callref, MT_RELEASE); + MsgHead(p, pc->callref, MT_RELEASE); *p++ = IE_CAUSE; *p++ = 0x2; @@ -607,64 +1289,352 @@ if (!(skb = l3_alloc_skb(l))) return; memcpy(skb_put(skb, l), tmp, l); - newl3state(st, 19); - st->l3.l3l2(st, DL_DATA, skb); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1); + newl3state(pc, 19); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3dss1_t310(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc); +} + +static void +l3dss1_t313(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0xE6; + l3dss1_disconnect_req(pc, pr, NULL); + pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc); +} + +static void +l3dss1_t308_1(struct l3_process *pc, u_char pr, void *arg) +{ + newl3state(pc, 19); + L3DelTimer(&pc->timer); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_2); +} + +static void +l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc); + release_l3_process(pc); +} + +static void +l3dss1_t318(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0x66; /* Timer expiry */ + pc->para.loc = 0; /* local */ + pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + newl3state(pc, 19); + l3dss1_message(pc, MT_RELEASE); + L3AddTimer(&pc->timer, T308, CC_T308_1); +} + +static void +l3dss1_t319(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->para.cause = 0x66; /* Timer expiry */ + pc->para.loc = 0; /* local */ + pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + newl3state(pc, 10); +} + +static void +l3dss1_restart(struct l3_process *pc, u_char pr, void *arg) +{ + L3DelTimer(&pc->timer); + pc->st->l3.l3l4(pc->st, CC_DLRL | INDICATION, pc); + release_l3_process(pc); +} + +static void +l3dss1_status(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + char tmp[64], *t; + int l; + struct sk_buff *skb = arg; + int cause, callState; + + cause = callState = -1; + p = skb->data; + t = tmp; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + l = *p++; + t += sprintf(t, "Status CR %x Cause:", pc->callref); + while (l--) { + cause = *p; + t += sprintf(t, " %2x", *p++); + } + } else + sprintf(t, "Status CR %x no Cause", pc->callref); + l3_debug(pc->st, tmp); + p = skb->data; + t = tmp; + t += sprintf(t, "Status state %x ", pc->state); + if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) { + p++; + if (1 == *p++) { + callState = *p; + t += sprintf(t, "peer state %x", *p); + } else + t += sprintf(t, "peer state len error"); + } else + sprintf(t, "no peer state"); + l3_debug(pc->st, tmp); + if (((cause & 0x7f) == 0x6f) && (callState == 0)) { + /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... + * if received MT_STATUS with cause == 0x6f and call + * state == 0, then we must set down layer 3 + */ + l3dss1_release_ind(pc, pr, arg); + } else + dev_kfree_skb(skb, FREE_READ); +} + +static void +l3dss1_facility(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + + p = skb->data; + if ((p = findie(p, skb->len, IE_FACILITY, 0))) { +#if HISAX_DE_AOC + l3dss1_parse_facility(pc, p); +#else + p = NULL; +#endif + } +} + +static void +l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb; + u_char tmp[32]; + u_char *p = tmp; + u_char i, l; + u_char *msg = pc->chan->setup.phone; + + MsgHead(p, pc->callref, MT_SUSPEND); + + *p++ = IE_CALLID; + l = *msg++; + if (l && (l <= 10)) { /* Max length 10 octets */ + *p++ = l; + for (i = 0; i < l; i++) + *p++ = *msg++; + } else { + l3_debug(pc->st, "SUS wrong CALLID len %d", l); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + newl3state(pc, 15); + L3AddTimer(&pc->timer, T319, CC_T319); +} + +static void +l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg) +{ + struct sk_buff *skb = arg; + + L3DelTimer(&pc->timer); + newl3state(pc, 0); + dev_kfree_skb(skb, FREE_READ); + pc->para.cause = -1; + pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc); + release_l3_process(pc); +} + +static void +l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg) +{ + u_char *p; + struct sk_buff *skb = arg; + int cause = -1; + + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + if (*p++ == 2) + pc->para.loc = *p++; + cause = *p & 0x7f; + } + dev_kfree_skb(skb, FREE_READ); + pc->para.cause = cause; + pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc); + newl3state(pc, 10); } static void -l3dss1_t310(struct PStack *st, u_char pr, void *arg) +l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_SETUP_ERR, NULL); + struct sk_buff *skb; + u_char tmp[32]; + u_char *p = tmp; + u_char i, l; + u_char *msg = pc->para.setup.phone; + + MsgHead(p, pc->callref, MT_RESUME); + + *p++ = IE_CALLID; + l = *msg++; + if (l && (l <= 10)) { /* Max length 10 octets */ + *p++ = l; + for (i = 0; i < l; i++) + *p++ = *msg++; + } else { + l3_debug(pc->st, "RES wrong CALLID len %d", l); + return; + } + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + l3_msg(pc->st, DL_DATA | REQUEST, skb); + newl3state(pc, 17); + L3AddTimer(&pc->timer, T319, CC_T319); } static void -l3dss1_t313(struct PStack *st, u_char pr, void *arg) +l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg) { - L3DelTimer(&st->l3.timer); - st->pa->cause = 0xE6; - l3dss1_disconnect_req(st, pr, NULL); - st->l3.l3l4(st, CC_CONNECT_ERR, NULL); + u_char *p; + struct sk_buff *skb = arg; + + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + pc->para.bchannel = p[2] & 0x3; + if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN)) + l3_debug(pc->st, "resume ack without bchannel"); + } else if (pc->debug & L3_DEB_WARN) + l3_debug(pc->st, "resume ack without bchannel"); + dev_kfree_skb(skb, FREE_READ); + pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc); + newl3state(pc, 10); } static void -l3dss1_t308_1(struct PStack *st, u_char pr, void *arg) +l3dss1_resume_rej(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 19); - L3DelTimer(&st->l3.timer); - l3dss1_message(st, MT_RELEASE); - L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2); + u_char *p; + struct sk_buff *skb = arg; + int cause = -1; + + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_CAUSE, 0))) { + p++; + if (*p++ == 2) + pc->para.loc = *p++; + cause = *p & 0x7f; + } + dev_kfree_skb(skb, FREE_READ); + pc->para.cause = cause; + newl3state(pc, 0); + pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc); + release_l3_process(pc); } static void -l3dss1_t308_2(struct PStack *st, u_char pr, void *arg) +l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg) { - newl3state(st, 0); - L3DelTimer(&st->l3.timer); - st->l3.l3l4(st, CC_RELEASE_ERR, NULL); + u_char tmp[32]; + u_char *p; + u_char ri, ch = 0, chan = 0; + int l; + struct sk_buff *skb = arg; + struct l3_process *up; + + newl3state(pc, 2); + L3DelTimer(&pc->timer); + p = skb->data; + if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) { + ri = p[2]; + l3_debug(pc->st, "Restart %x", ri); + } else { + l3_debug(pc->st, "Restart without restart IE"); + ri = 0x86; + } + p = skb->data; + if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) { + chan = p[2] & 3; + ch = p[2]; + if (pc->st->l3.debug) + l3_debug(pc->st, "Restart for channel %d", chan); + } + dev_kfree_skb(skb, FREE_READ); + newl3state(pc, 2); + up = pc->st->l3.proc; + while (up) { + if ((ri & 7) == 7) + up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + else if (up->para.bchannel == chan) + up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up); + up = up->next; + } + p = tmp; + MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE); + if (chan) { + *p++ = IE_CHANNEL_ID; + *p++ = 1; + *p++ = ch | 0x80; + } + *p++ = 0x79; /* RESTART Ind */ + *p++ = 1; + *p++ = ri; + l = p - tmp; + if (!(skb = l3_alloc_skb(l))) + return; + memcpy(skb_put(skb, l), tmp, l); + newl3state(pc, 0); + l3_msg(pc->st, DL_DATA | REQUEST, skb); } /* *INDENT-OFF* */ static struct stateentry downstatelist[] = { {SBIT(0), - CC_SETUP_REQ, l3dss1_setup_req}, + CC_SETUP | REQUEST, l3dss1_setup_req}, + {SBIT(0), + CC_RESUME | REQUEST, l3dss1_resume_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10), - CC_DISCONNECT_REQ, l3dss1_disconnect_req}, + CC_DISCONNECT | REQUEST, l3dss1_disconnect_req}, {SBIT(12), - CC_RELEASE_REQ, l3dss1_release_req}, + CC_RELEASE | REQUEST, l3dss1_release_req}, {ALL_STATES, - CC_DLRL, l3dss1_reset}, + CC_DLRL | REQUEST, l3dss1_reset}, + {ALL_STATES, + CC_RESTART | REQUEST, l3dss1_restart}, {SBIT(6), - CC_IGNORE, l3dss1_reset}, + CC_IGNORE | REQUEST, l3dss1_reset}, {SBIT(6), - CC_REJECT_REQ, l3dss1_reject_req}, + CC_REJECT | REQUEST, l3dss1_reject_req}, {SBIT(6), - CC_ALERTING_REQ, l3dss1_alert_req}, + CC_ALERTING | REQUEST, l3dss1_alert_req}, {SBIT(6) | SBIT(7), - CC_SETUP_RSP, l3dss1_setup_rsp}, + CC_SETUP | RESPONSE, l3dss1_setup_rsp}, + {SBIT(10), + CC_SUSPEND | REQUEST, l3dss1_suspend_req}, {SBIT(1), CC_T303, l3dss1_t303}, {SBIT(2), @@ -675,113 +1645,292 @@ CC_T313, l3dss1_t313}, {SBIT(11), CC_T305, l3dss1_t305}, + {SBIT(15), + CC_T319, l3dss1_t319}, + {SBIT(17), + CC_T318, l3dss1_t318}, {SBIT(19), CC_T308_1, l3dss1_t308_1}, {SBIT(19), CC_T308_2, l3dss1_t308_2}, }; -static int downsllen = sizeof(downstatelist) / -sizeof(struct stateentry); +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct stateentry)) static struct stateentry datastatelist[] = { {ALL_STATES, MT_STATUS_ENQUIRY, l3dss1_status_enq}, + {ALL_STATES, + MT_FACILITY, l3dss1_facility}, + {SBIT(19), + MT_STATUS, l3dss1_release_ind}, + {ALL_STATES, + MT_STATUS, l3dss1_status}, {SBIT(0) | SBIT(6), MT_SETUP, l3dss1_setup}, {SBIT(1) | SBIT(2), MT_CALL_PROCEEDING, l3dss1_call_proc}, + {SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_CALL_PROCEEDING, l3dss1_status_req}, {SBIT(1), MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack}, + {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_SETUP_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(1) | SBIT(2) | SBIT(3), MT_ALERTING, l3dss1_alerting}, + {SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_ALERTING, l3dss1_status_req}, {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), MT_RELEASE_COMPLETE, l3dss1_release_cmpl}, - {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(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | + SBIT(11) | SBIT(12) | SBIT(15) /* | SBIT(17) | SBIT(19)*/, MT_RELEASE, l3dss1_release}, - {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10), + {SBIT(19), MT_RELEASE, l3dss1_release_ind}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(15), MT_DISCONNECT, l3dss1_disconnect}, + {SBIT(11), + MT_DISCONNECT, l3dss1_release_req}, {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4), MT_CONNECT, l3dss1_connect}, + {SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19), + MT_CONNECT, l3dss1_status_req}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(11) | SBIT(19), + MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req}, {SBIT(8), MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack}, + {SBIT(15), + MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack}, + {SBIT(15), + MT_SUSPEND_REJECT, l3dss1_suspend_rej}, + {SBIT(17), + MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack}, + {SBIT(17), + MT_RESUME_REJECT, l3dss1_resume_rej}, + {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(19), + MT_INVALID, l3dss1_status_req}, }; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct stateentry)) + +static struct stateentry globalmes_list[] = +{ + {ALL_STATES, + MT_STATUS, l3dss1_status}, + {SBIT(0), + MT_RESTART, l3dss1_global_restart}, +/* {SBIT(1), + MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack}, +*/ +}; +#define GLOBALM_LEN \ + (sizeof(globalmes_list) / sizeof(struct stateentry)) /* *INDENT-ON* */ -static int datasllen = sizeof(datastatelist) / -sizeof(struct stateentry); +static void +global_handler(struct PStack *st, int mt, struct sk_buff *skb) +{ + int i; + struct l3_process *proc = st->l3.global; + + for (i = 0; i < GLOBALM_LEN; i++) + if ((mt == globalmes_list[i].primitive) && + ((1 << proc->state) & globalmes_list[i].state)) + break; + if (i == GLOBALM_LEN) { + dev_kfree_skb(skb, FREE_READ); + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "dss1 global state %d mt %x unhandled", + proc->state, mt); + } + return; + } else { + if (st->l3.debug & L3_DEB_STATE) { + l3_debug(st, "dss1 global %d mt %x", + proc->state, mt); + } + globalmes_list[i].rout(proc, mt, skb); + } +} static void dss1up(struct PStack *st, int pr, void *arg) { - int i, mt; + int i, mt, cr, cause, callState; + char *ptr; struct sk_buff *skb = arg; - char tmp[80]; + struct l3_process *proc; + switch (pr) { + case (DL_DATA | INDICATION): + case (DL_UNIT_DATA | INDICATION): + break; + case (DL_ESTABLISH | CONFIRM): + case (DL_ESTABLISH | INDICATION): + case (DL_RELEASE | INDICATION): + case (DL_RELEASE | CONFIRM): + l3_msg(st, pr, arg); + return; + break; + } if (skb->data[0] != PROTO_DIS_EURO) { if (st->l3.debug & L3_DEB_PROTERR) { - sprintf(tmp, "dss1up%sunexpected discriminator %x message len %ld state %d", - (pr == DL_DATA) ? " " : "(broadcast) ", - skb->data[0], skb->len, st->l3.state); - l3_debug(st, tmp); + l3_debug(st, "dss1up%sunexpected discriminator %x message len %d", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + skb->data[0], skb->len); } - SET_SKB_FREE(skb); dev_kfree_skb(skb, FREE_READ); return; } + cr = getcallref(skb->data); mt = skb->data[skb->data[1] + 2]; - for (i = 0; i < datasllen; i++) + if (!cr) { /* Global CallRef */ + global_handler(st, mt, skb); + return; + } else if (cr == -1) { /* Dummy Callref */ + dev_kfree_skb(skb, FREE_READ); + return; + } else if (!(proc = getl3proc(st, cr))) { + /* No transaction process exist, that means no call with + * this callreference is active + */ + if (mt == MT_SETUP) { + /* Setup creates a new transaction process */ + if (!(proc = new_l3_process(st, cr))) { + /* May be to answer with RELEASE_COMPLETE and + * CAUSE 0x2f "Resource unavailable", but this + * need a new_l3_process too ... arghh + */ + dev_kfree_skb(skb, FREE_READ); + return; + } + } else if (mt == MT_STATUS) { + cause = 0; + if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + cause = *ptr & 0x7f; + } + callState = 0; + if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) { + ptr++; + if (*ptr++ == 2) + ptr++; + callState = *ptr; + } + if (callState == 0) { + /* ETS 300-104 part 2.4.1 + * if setup has not been made and a message type + * MT_STATUS is received with call state == 0, + * we must send nothing + */ + dev_kfree_skb(skb, FREE_READ); + return; + } else { + /* ETS 300-104 part 2.4.2 + * if setup has not been made and a message type + * MT_STATUS is received with call state != 0, + * we must send MT_RELEASE_COMPLETE cause 101 + */ + dev_kfree_skb(skb, FREE_READ); + if ((proc = new_l3_process(st, cr))) { + proc->para.cause = 0x65; /* 101 */ + l3dss1_msg_without_setup(proc, 0, NULL); + } + return; + } + } else if (mt == MT_RELEASE_COMPLETE) { + dev_kfree_skb(skb, FREE_READ); + return; + } else { + /* ETS 300-104 part 2 + * if setup has not been made and a message type + * (except MT_SETUP and RELEASE_COMPLETE) is received, + * we must send MT_RELEASE_COMPLETE cause 81 */ + dev_kfree_skb(skb, FREE_READ); + if ((proc = new_l3_process(st, cr))) { + proc->para.cause = 0x51; /* 81 */ + l3dss1_msg_without_setup(proc, 0, NULL); + } + return; + } + } else if (!l3dss1_check_messagetype_validity(mt)) { + /* ETS 300-104 7.4.2, 8.4.2, 10.3.2, 11.4.2, 12.4.2, 13.4.2, + * 14.4.2... + * if setup has been made and invalid message type is received, + * we must send MT_STATUS cause 0x62 + */ + mt = MT_INVALID; /* sorry, not clean, but do the right thing ;-) */ + } + for (i = 0; i < DATASLLEN; i++) if ((mt == datastatelist[i].primitive) && - ((1 << st->l3.state) & datastatelist[i].state)) + ((1 << proc->state) & datastatelist[i].state)) break; - if (i == datasllen) { - SET_SKB_FREE(skb); + if (i == DATASLLEN) { dev_kfree_skb(skb, FREE_READ); if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "dss1up%sstate %d mt %x unhandled", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); - l3_debug(st, tmp); + l3_debug(st, "dss1up%sstate %d mt %x unhandled", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); } return; } else { if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "dss1up%sstate %d mt %x", - (pr == DL_DATA) ? " " : "(broadcast) ", - st->l3.state, mt); - l3_debug(st, tmp); + l3_debug(st, "dss1up%sstate %d mt %x", + (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", + proc->state, mt); } - datastatelist[i].rout(st, pr, skb); + datastatelist[i].rout(proc, pr, skb); } } static void dss1down(struct PStack *st, int pr, void *arg) { - int i; - char tmp[80]; + int i, cr; + struct l3_process *proc; + struct Channel *chan; - for (i = 0; i < downsllen; i++) + if (((DL_ESTABLISH | REQUEST) == pr) || ((DL_RELEASE | REQUEST) == pr)) { + l3_msg(st, pr, NULL); + return; + } else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) { + chan = arg; + cr = newcallref(); + cr |= 0x80; + if ((proc = new_l3_process(st, cr))) { + proc->chan = chan; + chan->proc = proc; + proc->para.setup = chan->setup; + proc->callref = cr; + } + } else { + proc = arg; + } + if (!proc) { + printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr); + return; + } + for (i = 0; i < DOWNSLLEN; i++) if ((pr == downstatelist[i].primitive) && - ((1 << st->l3.state) & downstatelist[i].state)) + ((1 << proc->state) & downstatelist[i].state)) break; - if (i == downsllen) { + if (i == DOWNSLLEN) { if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "dss1down state %d prim %d unhandled", - st->l3.state, pr); - l3_debug(st, tmp); + l3_debug(st, "dss1down state %d prim %d unhandled", + proc->state, pr); } } else { if (st->l3.debug & L3_DEB_STATE) { - sprintf(tmp, "dss1down state %d prim %d", - st->l3.state, pr); - l3_debug(st, tmp); + l3_debug(st, "dss1down state %d prim %d", + proc->state, pr); } - downstatelist[i].rout(st, pr, arg); + downstatelist[i].rout(proc, pr, arg); } } @@ -790,20 +1939,20 @@ { char tmp[64]; - st->l4.l4l3 = dss1down; + st->lli.l4l3 = dss1down; st->l2.l2l3 = dss1up; - st->l3.t303 = 4000; - st->l3.t304 = 30000; - st->l3.t305 = 30000; - st->l3.t308 = 4000; - st->l3.t310 = 30000; - st->l3.t313 = 4000; - st->l3.t318 = 4000; - st->l3.t319 = 4000; - st->l3.n_t303 = 1; - - if (st->l3.channr & 1) { - strcpy(tmp, dss1_revision); - printk(KERN_NOTICE "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp)); + st->l3.N303 = 1; + if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { + printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n"); + } else { + st->l3.global->state = 0; + st->l3.global->callref = 0; + st->l3.global->next = NULL; + st->l3.global->debug = L3_DEB_WARN; + st->l3.global->st = st; + st->l3.global->N303 = 1; + L3InitTimer(st->l3.global, &st->l3.global->timer); } + strcpy(tmp, dss1_revision); + printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp)); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/l3dss1.h linux/drivers/isdn/hisax/l3dss1.h --- v2.0.35/linux/drivers/isdn/hisax/l3dss1.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/l3dss1.h Sun Nov 15 10:32:58 1998 @@ -0,0 +1,75 @@ +/* $Id: l3dss1.h,v 1.3.2.3 1998/05/27 18:06:14 keil Exp $ + * + * DSS1 (Euro) D-channel protocol defines + * + * $Log: l3dss1.h,v $ + * Revision 1.3.2.3 1998/05/27 18:06:14 keil + * HiSax 3.0 + * + * Revision 1.3.2.2 1998/02/03 23:16:10 keil + * german AOC + * + * Revision 1.3.2.1 1997/10/17 22:10:52 keil + * new files on 2.0 + * + * Revision 1.3 1997/08/07 17:44:37 keil + * Fix RESTART + * + * Revision 1.2 1997/08/03 14:36:34 keil + * Implement RESTART procedure + * + * Revision 1.1 1997/07/27 21:08:38 keil + * new + * + * + * + */ +#define T303 4000 +#define T304 30000 +#define T305 30000 +#define T308 4000 +#define T310 30000 +#define T313 4000 +#define T318 4000 +#define T319 4000 + +/* + * Message-Types + */ + +#define MT_ALERTING 0x01 +#define MT_CALL_PROCEEDING 0x02 +#define MT_CONNECT 0x07 +#define MT_CONNECT_ACKNOWLEDGE 0x0f +#define MT_PROGRESS 0x03 +#define MT_SETUP 0x05 +#define MT_SETUP_ACKNOWLEDGE 0x0d +#define MT_RESUME 0x26 +#define MT_RESUME_ACKNOWLEDGE 0x2e +#define MT_RESUME_REJECT 0x22 +#define MT_SUSPEND 0x25 +#define MT_SUSPEND_ACKNOWLEDGE 0x2d +#define MT_SUSPEND_REJECT 0x21 +#define MT_USER_INFORMATION 0x20 +#define MT_DISCONNECT 0x45 +#define MT_RELEASE 0x4d +#define MT_RELEASE_COMPLETE 0x5a +#define MT_RESTART 0x46 +#define MT_RESTART_ACKNOWLEDGE 0x4e +#define MT_SEGMENT 0x60 +#define MT_CONGESTION_CONTROL 0x79 +#define MT_INFORMATION 0x7b +#define MT_FACILITY 0x62 +#define MT_NOTIFY 0x6e +#define MT_STATUS 0x7d +#define MT_STATUS_ENQUIRY 0x75 + +#define MT_INVALID 0xff + +#define IE_BEARER 0x04 +#define IE_CAUSE 0x08 +#define IE_CALLID 0x10 +#define IE_FACILITY 0x1c +#define IE_CALL_STATE 0x14 +#define IE_CHANNEL_ID 0x18 +#define IE_RESTART_IND 0x79 diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/lmgr.c linux/drivers/isdn/hisax/lmgr.c --- v2.0.35/linux/drivers/isdn/hisax/lmgr.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/lmgr.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,69 @@ +/* $Id: lmgr.c,v 1.1.2.5 1998/11/03 00:07:21 keil Exp $ + + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * Layermanagement module + * + * $Log: lmgr.c,v $ + * Revision 1.1.2.5 1998/11/03 00:07:21 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.4 1998/05/27 18:06:15 keil + * HiSax 3.0 + * + * Revision 1.1.2.3 1998/03/07 23:15:37 tsbogend + * made HiSax working on Linux/Alpha + * + * Revision 1.1.2.2 1997/11/15 18:54:19 keil + * cosmetics + * + * Revision 1.1.2.1 1997/10/17 22:10:53 keil + * new files on 2.0 + * + * Revision 1.1 1997/06/26 11:17:25 keil + * first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" + +static void +error_handling_dchan(struct PStack *st, int Error) +{ + switch (Error) { + case 'C': + case 'D': + case 'G': + case 'H': + st->l2.l2tei(st, MDL_ERROR | REQUEST, NULL); + break; + } +} + +static void +hisax_manager(struct PStack *st, int pr, void *arg) +{ + long Code; + + switch (pr) { + case (MDL_ERROR | INDICATION): + Code = (long) arg; + HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR", + "%c %s\n", (char)Code, + test_bit(FLG_LAPD, &st->l2.flag) ? + "D-channel" : "B-channel"); + if (test_bit(FLG_LAPD, &st->l2.flag)) + error_handling_dchan(st, Code); + break; + } +} + +void +setstack_manager(struct PStack *st) +{ + st->ma.layer = hisax_manager; +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/md5sums.asc linux/drivers/isdn/hisax/md5sums.asc --- v2.0.35/linux/drivers/isdn/hisax/md5sums.asc Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/md5sums.asc Sun Nov 15 10:32:58 1998 @@ -0,0 +1,29 @@ +-----BEGIN PGP SIGNED MESSAGE----- + +# 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. +# Read ../../../Documentation/isdn/HiSax.cert for more informations. +# +9bf10ea00044ef68391c9511cf96f9dc isac.c +1aa8f88a428a5a522c9256133b510004 isdnl1.c +b8382eb8a6d94bca10638f250cf441e0 isdnl2.c +3d7ebf2051d1a5aa4aacaf924a2442b2 isdnl3.c +15f5c1930545bbb8ec56bf905ae968ef tei.c +3cc38f121e5154c22536ec1f6e179fbf callc.c +1741371ba3e08468243bea3ce8a7820e cert.c +22849af1bcb15860e8426e416a037dac l3dss1.c +1a8a9f57880c23db0f49c7f141f546a1 l3_1tr6.c +b9d329bd178854b86ca37116889f7f3e elsa.c +# end of md5sums + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.3i +Charset: noconv + +iQCVAwUBNkIZQjpxHvX/mS9tAQEEZgP8DX7o3Y241UyCqPpgxB7J4NyWAg+p/wmF +NvYAMVLk10YZqUvA2h8kP7wXawNI1nR0Aj6fFz7Qf7KBcrPnSAxa9rwP32fuwrmx +qG2T0KUNxBhADP6lL6WuKRKA9NQ7a0nJVkrnO+yD+xq7eDHQQcm6zTrL02ogRR+1 +T1p8akJTsRA= +=yWR0 +-----END PGP SIGNATURE----- diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/mic.c linux/drivers/isdn/hisax/mic.c --- v2.0.35/linux/drivers/isdn/hisax/mic.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/mic.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,278 @@ +/* $Id: mic.c,v 1.1.2.5 1998/04/08 21:58:43 keil Exp $ + + * mic.c low level stuff for mic cards + * + * Copyright (C) 1997 + * + * Author Stephan von Krawczynski + * + * + * $Log: mic.c,v $ + * Revision 1.1.2.5 1998/04/08 21:58:43 keil + * New init code + * + * Revision 1.1.2.4 1998/02/17 15:39:20 keil + * fix reset problem + * + * Revision 1.1.2.3 1998/01/27 22:37:25 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:54 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:10:54 keil + * new files on 2.0 + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *mic_revision = "$Revision: 1.1.2.5 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define MIC_ISAC 2 +#define MIC_HSCX 1 +#define MIC_ADR 7 + +/* CARD_ADR (Write) */ +#define MIC_RESET 0x3 /* same as DOS driver */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.mic.adr, + cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.mic.adr, + cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \ + cs->hw.mic.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \ + cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \ + cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \ + cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +mic_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "mic: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_mic(struct IsdnCardState *cs) +{ + int bytecnt = 8; + + if (cs->hw.mic.cfg_reg) + release_region(cs->hw.mic.cfg_reg, bytecnt); +} + +static int +mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_io_mic(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &mic_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscx(cs); /* /RTSA := ISAC RST */ + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_mic(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, mic_revision); + printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_MIC) + return (0); + + bytecnt = 8; + cs->hw.mic.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR; + cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC; + cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX; + + if (check_region((cs->hw.mic.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.mic.cfg_reg, + cs->hw.mic.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn"); + } + + printk(KERN_INFO + "mic: defined at 0x%x IRQ %d\n", + cs->hw.mic.cfg_reg, + cs->irq); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &mic_card_msg; + ISACVersion(cs, "mic:"); + if (HscxVersion(cs, "mic:")) { + printk(KERN_WARNING + "mic: wrong HSCX versions check IO address\n"); + release_io_mic(cs); + return (0); + } + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c --- v2.0.35/linux/drivers/isdn/hisax/netjet.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/netjet.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,1154 @@ +/* $Id: netjet.c,v 1.1.2.10 1998/11/03 00:07:24 keil Exp $ + + * netjet.c low level stuff for Traverse Technologie NETJet ISDN cards + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Thanks to Traverse Technologie Australia for documents and informations + * + * + * $Log: netjet.c,v $ + * Revision 1.1.2.10 1998/11/03 00:07:24 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.9 1998/09/30 22:24:02 keil + * Fix missing line in setstack* + * + * Revision 1.1.2.8 1998/09/27 13:06:56 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.1.2.7 1998/05/27 18:06:17 keil + * HiSax 3.0 + * + * Revision 1.1.2.6 1998/04/08 22:05:23 keil + * Forgot PCI fix + * + * Revision 1.1.2.5 1998/04/08 21:49:29 keil + * New init; fix PCI for more as one card + * + * Revision 1.1.2.4 1998/01/27 22:37:27 keil + * fast io + * + * Revision 1.1.2.3 1997/12/01 09:09:57 keil + * IRQ bit clearing + * + * Revision 1.1.2.2 1997/11/27 12:32:01 keil + * Working netjet driver + * + * Revision 1.1.2.1 1997/11/15 18:58:13 keil + * new card + * + * + */ + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include +#include +#include + +#ifndef bus_to_virt +#define bus_to_virt (u_int *) +#endif + +#ifndef virt_to_bus +#define virt_to_bus (u_int) +#endif + +extern const char *CardType[]; + +const char *NETjet_revision = "$Revision: 1.1.2.10 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +/* PCI stuff */ +#define PCI_VENDOR_TRAVERSE_TECH 0xe159 +#define PCI_NETJET_ID 0x0001 + +#define NETJET_CTRL 0x00 +#define NETJET_DMACTRL 0x01 +#define NETJET_AUXCTRL 0x02 +#define NETJET_AUXDATA 0x03 +#define NETJET_IRQMASK0 0x04 +#define NETJET_IRQMASK1 0x05 +#define NETJET_IRQSTAT0 0x06 +#define NETJET_IRQSTAT1 0x07 +#define NETJET_DMA_READ_START 0x08 +#define NETJET_DMA_READ_IRQ 0x0c +#define NETJET_DMA_READ_END 0x10 +#define NETJET_DMA_READ_ADR 0x14 +#define NETJET_DMA_WRITE_START 0x18 +#define NETJET_DMA_WRITE_IRQ 0x1c +#define NETJET_DMA_WRITE_END 0x20 +#define NETJET_DMA_WRITE_ADR 0x24 +#define NETJET_PULSE_CNT 0x28 + +#define NETJET_ISAC_OFF 0xc0 +#define NETJET_ISACIRQ 0x10 +#define NETJET_IRQM0_READ 0x0c +#define NETJET_IRQM0_READ_1 0x04 +#define NETJET_IRQM0_READ_2 0x08 +#define NETJET_IRQM0_WRITE 0x03 +#define NETJET_IRQM0_WRITE_1 0x01 +#define NETJET_IRQM0_WRITE_2 0x02 + +#define NETJET_DMA_SIZE 512 + +#define HDLC_ZERO_SEARCH 0 +#define HDLC_FLAG_SEARCH 1 +#define HDLC_FLAG_FOUND 2 +#define HDLC_FRAME_FOUND 3 +#define HDLC_NULL 4 +#define HDLC_PART 5 +#define HDLC_FULL 6 + +#define HDLC_FLAG_VALUE 0x7e + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + long flags; + u_char ret; + + save_flags(flags); + cli(); + cs->hw.njet.auxd &= 0xfc; + cs->hw.njet.auxd |= (offset>>4) & 3; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2)); + restore_flags(flags); + return(ret); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + long flags; + + save_flags(flags); + cli(); + cs->hw.njet.auxd &= 0xfc; + cs->hw.njet.auxd |= (offset>>4) & 3; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value); + restore_flags(flags); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + cs->hw.njet.auxd &= 0xfc; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + insb(cs->hw.njet.isac, data, size); +} + +__u16 fcstab[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + cs->hw.njet.auxd &= 0xfc; + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); + outsb(cs->hw.njet.isac, data, size); +} + +void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill) +{ + u_int mask=0x000000ff, val = 0, *p=pos; + u_int i; + + val |= fill; + if (chan) { + val <<= 8; + mask <<= 8; + } + mask ^= 0xffffffff; + for (i=0; i bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } +} + +void +mode_tiger(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "Tiger mode %d bchan %d/%d", + mode, bc, bcs->channel); + bcs->mode = mode; + bcs->channel = bc; + switch (mode) { + case (L1_MODE_NULL): + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, 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, + bcs->hw.tiger.s_tot); + if ((cs->bcs[0].mode == L1_MODE_NULL) && + (cs->bcs[1].mode == L1_MODE_NULL)) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } + break; + case (L1_MODE_TRANS): + break; + case (L1_MODE_HDLC): + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, bc, 0xff); + bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH; + bcs->hw.tiger.r_tot = 0; + bcs->hw.tiger.r_bitcnt = 0; + bcs->hw.tiger.r_one = 0; + bcs->hw.tiger.r_err = 0; + bcs->hw.tiger.s_tot = 0; + if (! cs->hw.njet.dmactrl) { + fill_mem(bcs, bcs->hw.tiger.send, + NETJET_DMA_SIZE, !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; + test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); + break; + } + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "tiger: set %x %x %x %x/%x pulse=%d", + bytein(cs->hw.njet.base + NETJET_DMACTRL), + bytein(cs->hw.njet.base + NETJET_IRQMASK0), + bytein(cs->hw.njet.base + NETJET_IRQSTAT0), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); +} + +static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off) +{ + return(5); +} + +static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value) +{ +} + +static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) { + char tmp[128]; + char *t = tmp; + int i=count,j; + u_char *p = buf; + + t += sprintf(t, "tiger %s(%4d)", s, count); + while (i>0) { + if (i>16) + j=16; + else + j=i; + QuickHex(t, p, j); + debugl1(cs, tmp); + p += j; + i -= j; + t = tmp; + t += sprintf(t, "tiger %s ", s); + } +} + +#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \ + bitcnt++;\ + s_val >>= 1;\ + if (val & 1) {\ + s_one++;\ + s_val |= 0x80;\ + } else {\ + s_one = 0;\ + s_val &= 0x7f;\ + }\ + if (bitcnt==8) {\ + bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\ + bitcnt = 0;\ + }\ + if (s_one == 5) {\ + s_val >>= 1;\ + s_val &= 0x7f;\ + bitcnt++;\ + s_one = 0;\ + }\ + if (bitcnt==8) {\ + bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\ + bitcnt = 0;\ + }\ + val >>= 1;\ + } + +static int make_raw_data(struct BCState *bcs) { + register u_int i,s_cnt=0; + register u_char j; + register u_char val; + register u_char s_one = 0; + register u_char s_val = 0; + register u_char bitcnt = 0; + u_int fcs; + + if (!bcs->tx_skb) { + debugl1(bcs->cs, "tiger make_raw: NULL skb"); + return(1); + } + bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE; + fcs = PPP_INITFCS; + for (i=0; itx_skb->len; i++) { + val = bcs->tx_skb->data[i]; + fcs = PPP_FCS (fcs, val); + MAKE_RAW_BYTE; + } + fcs ^= 0xffff; + val = fcs & 0xff; + MAKE_RAW_BYTE; + val = (fcs>>8) & 0xff; + MAKE_RAW_BYTE; + val = HDLC_FLAG_VALUE; + for (j=0; j<8; j++) { + bitcnt++; + s_val >>= 1; + if (val & 1) + s_val |= 0x80; + else + s_val &= 0x7f; + if (bitcnt==8) { + bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + bitcnt = 0; + } + val >>= 1; + } + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger make_raw: in %ld out %d.%d", + bcs->tx_skb->len, s_cnt, bitcnt); + if (bitcnt) { + while (8>bitcnt++) { + s_val >>= 1; + s_val |= 0x80; + } + bcs->hw.tiger.sendbuf[s_cnt++] = s_val; + } + bcs->hw.tiger.sendcnt = s_cnt; + bcs->tx_cnt -= bcs->tx_skb->len; + bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf; + return(0); +} + +static void got_frame(struct BCState *bcs, int count) { + struct sk_buff *skb; + + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "TIGER: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->event |= 1 << B_RCVBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME) + printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec"); +} + + + +static void read_raw(struct BCState *bcs, u_int *buf, int cnt){ + int i; + register u_char j; + register u_char val; + u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -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; + register u_int bitcnt = bcs->hw.tiger.r_bitcnt; + u_int *p = buf; + + for (i=0;ichannel ? ((*p>>8) & 0xff) : (*p & 0xff); + p++; + if (p > pend) + p = bcs->hw.tiger.rec; + if (val == 0xff) { + state = HDLC_ZERO_SEARCH; + bcs->hw.tiger.r_tot++; + bitcnt = 0; + r_one = 0; + continue; + } + for (j=0;j<8;j++) { + if (state == HDLC_ZERO_SEARCH) { + if (val & 1) { + r_one++; + } else { + r_one=0; + state= HDLC_FLAG_SEARCH; + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger read_raw: zBit(%d,%d,%d) %x", + bcs->hw.tiger.r_tot,i,j,val); + } + } else if (state == HDLC_FLAG_SEARCH) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } + } else { + if (r_one==6) { + bitcnt=0; + r_val=0; + state=HDLC_FLAG_FOUND; + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger read_raw: flag(%d,%d,%d) %x", + bcs->hw.tiger.r_tot,i,j,val); + } + r_one=0; + } + } else if (state == HDLC_FLAG_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + } else { + r_val >>= 1; + r_val |= 0x80; + bitcnt++; + } + } else { + if (r_one==6) { + bitcnt=0; + r_val=0; + r_one=0; + val >>= 1; + continue; + } else if (r_one!=5) { + r_val >>= 1; + r_val &= 0x7f; + bitcnt++; + } + r_one=0; + } + if ((state != HDLC_ZERO_SEARCH) && + !(bitcnt & 7)) { + state=HDLC_FRAME_FOUND; + bcs->hw.tiger.r_fcs = PPP_INITFCS; + bcs->hw.tiger.rcvbuf[0] = r_val; + bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val); + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x", + bcs->hw.tiger.r_tot,i,j,r_val,val, + bcs->cs->hw.njet.irqstat0); + } + } else if (state == HDLC_FRAME_FOUND) { + if (val & 1) { + r_one++; + if (r_one>6) { + state=HDLC_ZERO_SEARCH; + bitcnt=0; + } else { + r_val >>= 1; + r_val |= 0x80; + bitcnt++; + } + } else { + if (r_one==6) { + r_val=0; + r_one=0; + bitcnt++; + if (bitcnt & 7) { + debugl1(bcs->cs, "tiger: frame not byte aligned"); + state=HDLC_FLAG_SEARCH; + bcs->hw.tiger.r_err++; + } else { + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x", + i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0); + if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) { + got_frame(bcs, (bitcnt>>3)-3); + } else + if (bcs->cs->debug) { + debugl1(bcs->cs, "tiger FCS error"); + printframe(bcs->cs, bcs->hw.tiger.rcvbuf, + (bitcnt>>3)-1, "rec"); + bcs->hw.tiger.r_err++; + } + state=HDLC_FLAG_FOUND; + } + bitcnt=0; + } else if (r_one==5) { + val >>= 1; + r_one=0; + continue; + } else { + r_val >>= 1; + r_val &= 0x7f; + bitcnt++; + } + r_one=0; + } + if ((state == HDLC_FRAME_FOUND) && + !(bitcnt & 7)) { + if ((bitcnt>>3)>=HSCX_BUFMAX) { + debugl1(bcs->cs, "tiger: frame to big"); + r_val=0; + state=HDLC_FLAG_SEARCH; + bcs->hw.tiger.r_err++; + } else { + bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val; + bcs->hw.tiger.r_fcs = + PPP_FCS (bcs->hw.tiger.r_fcs, r_val); + } + } + } + val >>= 1; + } + bcs->hw.tiger.r_tot++; + } + bcs->hw.tiger.r_state = state; + bcs->hw.tiger.r_one = r_one; + bcs->hw.tiger.r_val = r_val; + bcs->hw.tiger.r_bitcnt = bitcnt; +} + +static void read_tiger(struct IsdnCardState *cs) { + u_int *p; + int cnt = NETJET_DMA_SIZE/2; + + if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) { + debugl1(cs,"tiger warn read double dma %x/%x", + cs->hw.njet.irqstat0, cs->hw.njet.last_is0); + return; + } else { + cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ; + 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; + else + p = cs->bcs[0].hw.tiger.rec + cnt - 1; + if (cs->bcs[0].mode == L1_MODE_HDLC) + read_raw(cs->bcs, p, cnt); + if (cs->bcs[1].mode == L1_MODE_HDLC) + read_raw(cs->bcs + 1, p, cnt); + cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_READ; +} + +static void write_raw(struct BCState *bcs, u_int *buf, int cnt); + +static void fill_dma(struct BCState *bcs) +{ + register u_int *p, *sp; + register int cnt; + + if (!bcs->tx_skb) + return; + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger fill_dma1: c%d %4x", bcs->channel, + bcs->Flag); + if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag)) + return; + if (make_raw_data(bcs)) + return; + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger fill_dma2: c%d %4x", bcs->channel, + bcs->Flag); + if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) { + write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free); + } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) { + p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR)); + sp = bcs->hw.tiger.sendp; + if (p == bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send -1; + if (sp == bcs->hw.tiger.s_end) + sp = bcs->hw.tiger.send -1; + cnt = p - sp; + if (cnt <0) { + write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free); + } else { + p++; + cnt++; + if (p > bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + p++; + cnt++; + if (p > bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + write_raw(bcs, p, bcs->hw.tiger.free - cnt); + } + } else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) { + p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR)); + cnt = bcs->hw.tiger.s_end - p; + if (cnt < 2) { + p = bcs->hw.tiger.send + 1; + cnt = NETJET_DMA_SIZE/2 - 2; + } else { + p++; + p++; + if (cnt <= (NETJET_DMA_SIZE/2)) + cnt += NETJET_DMA_SIZE/2; + cnt--; + cnt--; + } + write_raw(bcs, p, cnt); + } + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger fill_dma3: c%d %4x", bcs->channel, + bcs->Flag); +} + +static void write_raw(struct BCState *bcs, u_int *buf, int cnt) { + u_int mask, val, *p=buf; + u_int i, s_cnt; + + if (cnt <= 0) + return; + if (test_bit(BC_FLG_BUSY, &bcs->Flag)) { + if (bcs->hw.tiger.sendcnt> cnt) { + s_cnt = cnt; + bcs->hw.tiger.sendcnt -= cnt; + } else { + s_cnt = bcs->hw.tiger.sendcnt; + bcs->hw.tiger.sendcnt = 0; + } + if (bcs->channel) + mask = 0xffff00ff; + else + mask = 0xffffff00; + for (i=0; ichannel ? ((bcs->hw.tiger.sp[i] <<8) & 0xff00) : + (bcs->hw.tiger.sp[i]); + *p &= mask; + *p++ |= val; + if (p>bcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } + bcs->hw.tiger.s_tot += s_cnt; + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel, + (u_int)buf, (u_int)p, s_cnt, cnt, + bcs->hw.tiger.sendcnt, bcs->cs->hw.njet.irqstat0); + if (bcs->cs->debug & L1_DEB_HSCX_FIFO) + printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd"); + bcs->hw.tiger.sp += s_cnt; + bcs->hw.tiger.sendp = p; + if (!bcs->hw.tiger.sendcnt) { + if (!bcs->tx_skb) { + debugl1(bcs->cs,"tiger write_raw: NULL skb s_cnt %d", s_cnt); + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + } + 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)) + test_and_set_bit(BC_FLG_HALF, &bcs->Flag); + else { + test_and_clear_bit(BC_FLG_HALF, &bcs->Flag); + test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag); + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + fill_dma(bcs); + } else { + mask ^= 0xffffffff; + if (s_cnt < cnt) { + for (i=s_cnt; ibcs->hw.tiger.s_end) + p = bcs->hw.tiger.send; + } + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs, "tiger write_raw: fill rest %d", + cnt - s_cnt); + } + bcs->event |= 1 << B_XMTBUFREADY; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + } else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) { + test_and_set_bit(BC_FLG_HALF, &bcs->Flag); + fill_mem(bcs, buf, cnt, bcs->channel, 0xff); + bcs->hw.tiger.free += cnt; + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger write_raw: fill half"); + } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) { + test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag); + fill_mem(bcs, buf, cnt, bcs->channel, 0xff); + if (bcs->cs->debug & L1_DEB_HSCX) + debugl1(bcs->cs,"tiger write_raw: fill full"); + } +} + +static void write_tiger(struct IsdnCardState *cs) { + u_int *p, cnt = NETJET_DMA_SIZE/2; + + if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) { + debugl1(cs,"tiger warn write double dma %x/%x", + cs->hw.njet.irqstat0, cs->hw.njet.last_is0); + return; + } else { + cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE; + 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; + else + p = cs->bcs[0].hw.tiger.send + cnt - 1; + if (cs->bcs[0].mode == L1_MODE_HDLC) + write_raw(cs->bcs, p, cnt); + if (cs->bcs[1].mode == L1_MODE_HDLC) + write_raw(cs->bcs + 1, p, cnt); + cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_WRITE; +} + +static void +tiger_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); + st->l1.bcs->tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + mode_tiger(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + + +void +close_tigerstate(struct BCState *bcs) +{ + mode_tiger(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.tiger.rcvbuf) { + kfree(bcs->hw.tiger.rcvbuf); + bcs->hw.tiger.rcvbuf = NULL; + } + if (bcs->hw.tiger.sendbuf) { + kfree(bcs->hw.tiger.sendbuf); + bcs->hw.tiger.sendbuf = NULL; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + dev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_tigerstate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.rcvbuf\n"); + return (1); + } + if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for tiger.sendbuf\n"); + return (1); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + bcs->hw.tiger.sendcnt = 0; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +int +setstack_tiger(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_tigerstate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = tiger_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + + +__initfunc(void +inittiger(struct IsdnCardState *cs)) +{ + if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * 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[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)); + 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)); + 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), + 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)); + 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)); + 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), + cs->hw.njet.base + NETJET_DMA_WRITE_IRQ); + outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 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), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); + cs->hw.njet.last_is0 = 0; + cs->bcs[0].BC_SetStack = setstack_tiger; + cs->bcs[1].BC_SetStack = setstack_tiger; + cs->bcs[0].BC_Close = close_tigerstate; + cs->bcs[1].BC_Close = close_tigerstate; +} + +void +releasetiger(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.tiger.send) { + kfree(cs->bcs[0].hw.tiger.send); + cs->bcs[0].hw.tiger.send = NULL; + } + if (cs->bcs[1].hw.tiger.send) { + cs->bcs[1].hw.tiger.send = NULL; + } + if (cs->bcs[0].hw.tiger.rec) { + kfree(cs->bcs[0].hw.tiger.rec); + cs->bcs[0].hw.tiger.rec = NULL; + } + if (cs->bcs[1].hw.tiger.rec) { + cs->bcs[1].hw.tiger.rec = NULL; + } +} + +static void +netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, sval; + long flags; + + if (!cs) { + printk(KERN_WARNING "NETjet: Spurious interrupt!\n"); + return; + } + if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) & + NETJET_ISACIRQ)) { + val = ReadISAC(cs, ISAC_ISTA); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "tiger: i1 %x %x", sval, val); + if (val) { + isac_interrupt(cs, val); + WriteISAC(cs, ISAC_MASK, 0xFF); + WriteISAC(cs, ISAC_MASK, 0x0); + } + } + save_flags(flags); + cli(); + if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) { + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + restore_flags(flags); + return; + } + cs->hw.njet.irqstat0 = sval; + restore_flags(flags); +/* debugl1(cs, "tiger: ist0 %x %x %x %x/%x pulse=%d", + sval, + bytein(cs->hw.njet.base + NETJET_DMACTRL), + bytein(cs->hw.njet.base + NETJET_IRQMASK0), + inl(cs->hw.njet.base + NETJET_DMA_READ_ADR), + inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR), + bytein(cs->hw.njet.base + NETJET_PULSE_CNT)); +*/ +/* cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30; +*/ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0); +/* byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0); +*/ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) + read_tiger(cs); + if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) + write_tiger(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + restore_flags(flags); + +/* if (!testcnt--) { + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_DMACTRL, + cs->hw.njet.dmactrl); + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + } +*/ +} + +static void +reset_netjet(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + sti(); + cs->hw.njet.ctrl_reg = 0xff; /* Reset On */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */ + byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (10 * HZ) / 1000; /* Timeout 10ms */ + schedule(); + restore_flags(flags); + cs->hw.njet.auxd = 0; + cs->hw.njet.dmactrl = 0; + byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ); + byteout(cs->hw.njet.auxa, cs->hw.njet.auxd); +} + +void +release_io_netjet(struct IsdnCardState *cs) +{ + byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0); + byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0); + releasetiger(cs); + release_region(cs->hw.njet.base, 256); +} + + +static int +NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_netjet(cs); + return(0); + case CARD_RELEASE: + release_io_netjet(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &netjet_interrupt, + I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); + case CARD_INIT: + inittiger(cs); + clear_pending_isac_ints(cs); + initisac(cs); + /* Reenable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + + + +static int pci_index __initdata = 0; + +__initfunc(int +setup_netjet(struct IsdnCard *card)) +{ + int bytecnt; + struct IsdnCardState *cs = card->cs; + char tmp[64]; +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr, found; +#endif + + strcpy(tmp, NETjet_revision); + printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NETJET) + return(0); +#if CONFIG_PCI + found = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH, + PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + found = 1; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + if (found) + break; + } + if (!found) { + printk(KERN_WARNING "NETjet: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n"); + return(0); + } + if (!pci_ioaddr) { + printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n"); + return(0); + } + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.njet.base = pci_ioaddr; + cs->hw.njet.auxa = pci_ioaddr + NETJET_AUXDATA; + cs->hw.njet.isac = pci_ioaddr | NETJET_ISAC_OFF; + cs->irq = pci_irq; + bytecnt = 256; +#else + printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n"); + printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO + "NETjet: PCI card configured at 0x%x IRQ %d\n", + cs->hw.njet.base, cs->irq); + if (check_region(cs->hw.njet.base, bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.njet.base, + cs->hw.njet.base + bytecnt); + return (0); + } else { + request_region(cs->hw.njet.base, bytecnt, "netjet isdn"); + } + reset_netjet(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &dummyrr; + cs->BC_Write_Reg = &dummywr; + cs->BC_Send_Data = &fill_dma; + cs->cardmsg = &NETjet_card_msg; + ISACVersion(cs, "NETjet:"); + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/niccy.c linux/drivers/isdn/hisax/niccy.c --- v2.0.35/linux/drivers/isdn/hisax/niccy.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/niccy.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,406 @@ +/* $Id: niccy.c,v 1.1.2.4 1998/04/16 19:18:19 keil Exp $ + + * niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and + * compatible (SAGEM cybermodem) + * + * Author Karsten Keil + * + * Thanks to Dr. Neuhaus and SAGEM for informations + * + * $Log: niccy.c,v $ + * Revision 1.1.2.4 1998/04/16 19:18:19 keil + * need config.h + * + * Revision 1.1.2.3 1998/04/08 22:05:26 keil + * Forgot PCI fix + * + * Revision 1.1.2.2 1998/04/08 21:48:23 keil + * New init; working Niccy PCI + * + * Revision 1.1.2.1 1998/02/11 14:23:20 keil + * support for Dr Neuhaus Niccy PnP and PCI + * + * + */ + + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; +const char *niccy_revision = "$Revision: 1.1.2.4 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISAC_PCI_DATA 0 +#define HSCX_PCI_DATA 1 +#define ISAC_PCI_ADDR 2 +#define HSCX_PCI_ADDR 3 +#define ISAC_PNP 0 +#define HSCX_PNP 1 + +/* SUB Types */ +#define NICCY_PNP 1 +#define NICCY_PCI 2 + +/* PCI stuff */ +#define PCI_VENDOR_DR_NEUHAUS 0x1267 +#define PCI_NICCY_ID 0x1016 +#define PCI_IRQ_CTRL_REG 0x38 +#define PCI_IRQ_ENABLE 0x1f00 +#define PCI_IRQ_DISABLE 0xff0000 +#define PCI_IRQ_ASSERT 0x800000 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.niccy.hscx_ale, + cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value); +} + +#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \ + cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "Niccy: Spurious interrupt!\n"); + return; + } + if (cs->subtyp == NICCY_PCI) { + int ival; + ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + if (!(ival & PCI_IRQ_ASSERT)) /* IRQ not for us (shared) */ + return; + outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + } + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0); + writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0); + } + if (stat & 2) { + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0); + } +} + +void +release_io_niccy(struct IsdnCardState *cs) +{ + if (cs->subtyp == NICCY_PCI) { + int val; + + val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + val &= PCI_IRQ_DISABLE; + outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + release_region(cs->hw.niccy.cfg_reg, 0x80); + release_region(cs->hw.niccy.isac, 4); + } else { + release_region(cs->hw.niccy.isac, 2); + release_region(cs->hw.niccy.isac_ale, 2); + } +} + +static void +niccy_reset(struct IsdnCardState *cs) +{ + int val, nval; + + val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + nval = val | PCI_IRQ_ENABLE; + outl(nval, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG); + + inithscxisac(cs, 3); +} + +static int +niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + int imode; + + switch (mt) { + case CARD_RESET: + niccy_reset(cs); + return(0); + case CARD_RELEASE: + release_io_niccy(cs); + return(0); + case CARD_SETIRQ: + if (cs->subtyp == NICCY_PCI) + imode = I4L_IRQ_FLAG | SA_SHIRQ; + else + imode = I4L_IRQ_FLAG; + return(request_irq(cs->irq, &niccy_interrupt, + imode, "HiSax", cs)); + break; + case CARD_INIT: + niccy_reset(cs); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static int pci_index __initdata = 0; + +__initfunc(int +setup_niccy(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, niccy_revision); + printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_NICCY) + return (0); + + if (card->para[1]) { + cs->hw.niccy.isac = card->para[1] + ISAC_PNP; + cs->hw.niccy.hscx = card->para[1] + HSCX_PNP; + cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP; + cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP; + cs->hw.niccy.cfg_reg = 0; + cs->subtyp = NICCY_PNP; + cs->irq = card->para[0]; + if (check_region((cs->hw.niccy.isac), 2)) { + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 1); + return (0); + } else + request_region(cs->hw.niccy.isac, 2, "niccy data"); + if (check_region((cs->hw.niccy.isac_ale), 2)) { + printk(KERN_WARNING + "HiSax: %s address port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac_ale, + cs->hw.niccy.isac_ale + 1); + release_region(cs->hw.niccy.isac, 2); + return (0); + } else + request_region(cs->hw.niccy.isac_ale, 2, "niccy addr"); + } else { +#if CONFIG_PCI + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_ioaddr; + + cs->subtyp = 0; + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS, + PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) + cs->subtyp = NICCY_PCI; + else + break; + /* get IRQ */ + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + /* get IO pci AMCC address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + if (!pci_ioaddr) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n"); + return(0); + } + cs->hw.niccy.cfg_reg = pci_ioaddr & ~3 ; + /* get IO address */ + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr); + if (cs->subtyp) + break; + } + if (!cs->subtyp) { + printk(KERN_WARNING "Niccy: No PCI card found\n"); + return(0); + } + pci_index++; + if (!pci_irq) { + printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n"); + return(0); + } + if (!pci_ioaddr) { + printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n"); + return(0); + } + + pci_ioaddr &= ~3; /* remove io/mem flag */ + cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA; + cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR; + cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA; + cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR; + cs->irq = pci_irq; + if (check_region((cs->hw.niccy.isac), 4)) { + printk(KERN_WARNING + "HiSax: %s data port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.isac, + cs->hw.niccy.isac + 4); + return (0); + } else + request_region(cs->hw.niccy.isac, 4, "niccy"); + if (check_region(cs->hw.niccy.cfg_reg, 0x80)) { + printk(KERN_WARNING + "HiSax: %s pci port %x-%x already in use\n", + CardType[card->typ], + cs->hw.niccy.cfg_reg, + cs->hw.niccy.cfg_reg + 0x80); + release_region(cs->hw.niccy.isac, 4); + return (0); + } else { + request_region(cs->hw.niccy.cfg_reg, 0x80, "niccy pci"); + } +#else + printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n"); + printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n"); + return (0); +#endif /* CONFIG_PCI */ + } + printk(KERN_INFO + "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n", + CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI", + cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &niccy_card_msg; + ISACVersion(cs, "Niccy:"); + if (HscxVersion(cs, "Niccy:")) { + printk(KERN_WARNING + "Niccy: wrong HSCX versions check IO address\n"); + release_io_niccy(cs); + return (0); + } + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/q931.c linux/drivers/isdn/hisax/q931.c --- v2.0.35/linux/drivers/isdn/hisax/q931.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/q931.c Sun Nov 15 10:32:58 1998 @@ -1,4 +1,4 @@ -/* $Id: q931.c,v 1.5 1997/04/06 22:56:43 keil Exp $ +/* $Id: q931.c,v 1.5.2.3 1998/11/03 00:07:28 keil Exp $ * q931.c code to decode ITU Q.931 call control messages * @@ -14,6 +14,19 @@ * * * $Log: q931.c,v $ + * Revision 1.5.2.3 1998/11/03 00:07:28 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.5.2.2 1998/10/25 18:16:32 fritz + * Replaced some read-only variables by defines. + * + * Revision 1.5.2.1 1997/10/17 22:14:20 keil + * update to last hisax version + * + * Revision 1.6 1997/07/27 21:09:44 keil + * move functions to isdnl3.c + * * Revision 1.5 1997/04/06 22:56:43 keil * Some cosmetic changes * @@ -37,44 +50,6 @@ #include "hisax.h" #include "l3_1tr6.h" -u_char * -findie(u_char * p, int size, u_char ie, int wanted_set) -{ - int l, codeset, maincodeset; - u_char *pend = p + size; - - /* skip protocol discriminator, callref and message type */ - p++; - l = (*p++) & 0xf; - p += l; - p++; - codeset = 0; - maincodeset = 0; - /* while there are bytes left... */ - while (p < pend) { - if ((*p & 0xf0) == 0x90) { - codeset = *p & 0x07; - if (!(*p & 0x08)) - maincodeset = codeset; - } - if (*p & 0x80) - p++; - else { - if (codeset == wanted_set) { - if (*p == ie) - return (p); - if (*p > ie) - return (NULL); - } - p++; - l = *p++; - p += l; - codeset = maincodeset; - } - } - return (NULL); -} - void iecpy(u_char * dest, u_char * iestart, int ieoffset) { @@ -88,14 +63,6 @@ *dest++ = '\0'; } -int -getcallref(u_char * p) -{ - p++; /* prot discr */ - p++; /* callref length */ - return (*p); /* assuming one-byte callref */ -} - /* * According to Table 4-2/Q.931 */ @@ -202,7 +169,7 @@ {MT_N0_CLO_ACK, "CLOse ACKnowledge"} }; -int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType)); +#define MT_N0_LEN (sizeof(mt_n0) / sizeof(struct MessageType)) static struct MessageType mt_n1[] = @@ -239,7 +206,7 @@ {MT_N1_STAT, "STATus"} }; -int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType)); +#define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType)) static struct MessageType fac_1tr6[] = { @@ -263,9 +230,7 @@ {FAC_Rueckwechsel, "Rueckwechsel"}, {FAC_Umleitung, "Umleitung"} }; -int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType)); - - +#define FAC_1TR6_LEN (sizeof(fac_1tr6) / sizeof(struct MessageType)) static int prbits(char *dest, u_char b, int start, int len) @@ -968,7 +933,7 @@ {WE0_userInfo, "User Info", general} }; -static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement)); +#define WE_0_LEN (sizeof(we_0) / sizeof(struct InformationElement)) static struct InformationElement we_6[] = { @@ -980,7 +945,7 @@ {WE6_statusCalled, "Status Called", general}, {WE6_addTransAttr, "Additional Transmission Attributes", general} }; -static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement)); +#define WE_6_LEN (sizeof(we_6) / sizeof(struct InformationElement)) int QuickHex(char *txt, u_char * p, int cnt) @@ -1007,39 +972,92 @@ } void -LogFrame(struct IsdnCardState *sp, u_char * buf, int size) +LogFrame(struct IsdnCardState *cs, u_char * buf, int size) { char *dp; if (size < 1) return; - dp = sp->dlogspace; - if (size < 4096 / 3 - 10) { - dp += sprintf(dp, "HEX:"); + dp = cs->dlog; + if (size < MAX_DLOG_SPACE / 3 - 10) { + *dp++ = 'H'; + *dp++ = 'E'; + *dp++ = 'X'; + *dp++ = ':'; dp += QuickHex(dp, buf, size); dp--; *dp++ = '\n'; *dp = 0; + HiSax_putstatus(cs, NULL, cs->dlog); } else - sprintf(dp, "LogFrame: warning Frame too big (%d)\n", - size); - HiSax_putstatus(sp, sp->dlogspace); + HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size); } void -dlogframe(struct IsdnCardState *sp, u_char * buf, int size, char *comment) +dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir) { - u_char *bend = buf + size; + u_char *bend, *buf; char *dp; unsigned char pd, cr_l, cr, mt; - int i, cs = 0, cs_old = 0, cs_fest = 0; + unsigned char sapi, tei, ftyp; + int i, cset = 0, cs_old = 0, cs_fest = 0; + int size, finish = 0; - if (size < 1) + if (skb->len < 3) return; /* display header */ - dp = sp->dlogspace; - dp += sprintf(dp, "%s\n", comment); - + dp = cs->dlog; + dp += jiftime(dp, jiffies); + *dp++ = ' '; + sapi = skb->data[0] >> 2; + tei = skb->data[1] >> 1; + ftyp = skb->data[2]; + buf = skb->data; + dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network"); + size = skb->len; + + if (tei == GROUP_TEI) { + if (sapi == CTRL_SAPI) { /* sapi 0 */ + if (ftyp == 3) { + dp += sprintf(dp, "broadcast\n"); + buf += 3; + size -= 3; + } else { + dp += sprintf(dp, "no UI broadcast\n"); + finish = 1; + } + } else if (sapi == TEI_SAPI) { + dp += sprintf(dp, "tei managment\n"); + finish = 1; + } else { + dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi); + finish = 1; + } + } else { + if (sapi == CTRL_SAPI) { + if (!(ftyp & 1)) { /* IFrame */ + dp += sprintf(dp, "with tei %d\n", tei); + buf += 4; + size -= 4; + } else { + dp += sprintf(dp, "SFrame with tei %d\n", tei); + finish = 1; + } + } else { + dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei); + finish = 1; + } + } + bend = skb->data + skb->len; + if (buf >= bend) { + dp += sprintf(dp, "frame too short\n"); + finish = 1; + } + if (finish) { + *dp = 0; + HiSax_putstatus(cs, NULL, cs->dlog); + return; + } if ((0xfe & buf[0]) == PROTO_DIS_N0) { /* 1TR6 */ /* locate message type */ pd = *buf++; @@ -1050,11 +1068,11 @@ cr = 0; mt = *buf++; if (pd == PROTO_DIS_N0) { /* N0 */ - for (i = 0; i < mt_n0_len; i++) + for (i = 0; i < MT_N0_LEN; i++) if (mt_n0[i].nr == mt) break; /* display message type if it exists */ - if (i == mt_n0_len) + if (i == MT_N0_LEN) dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n", cr & 0x7f, (cr & 0x80) ? "called" : "caller", size, mt); @@ -1063,11 +1081,11 @@ cr & 0x7f, (cr & 0x80) ? "called" : "caller", size, mt_n0[i].descr); } else { /* N1 */ - for (i = 0; i < mt_n1_len; i++) + for (i = 0; i < MT_N1_LEN; i++) if (mt_n1[i].nr == mt) break; /* display message type if it exists */ - if (i == mt_n1_len) + if (i == MT_N1_LEN) dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n", cr & 0x7f, (cr & 0x80) ? "called" : "caller", size, mt); @@ -1084,8 +1102,8 @@ switch ((*buf >> 4) & 7) { case 1: dp += sprintf(dp, " Shift %x\n", *buf & 0xf); - cs_old = cs; - cs = *buf & 7; + cs_old = cset; + cset = *buf & 7; cs_fest = *buf & 8; break; case 3: @@ -1109,33 +1127,33 @@ continue; } /* No, locate it in the table */ - if (cs == 0) { - for (i = 0; i < we_0_len; i++) + if (cset == 0) { + for (i = 0; i < WE_0_LEN; i++) if (*buf == we_0[i].nr) break; /* When found, give appropriate msg */ - if (i != we_0_len) { + if (i != WE_0_LEN) { dp += sprintf(dp, " %s\n", we_0[i].descr); dp += we_0[i].f(dp, buf); } else - dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]); - } else if (cs == 6) { - for (i = 0; i < we_6_len; i++) + dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]); + } else if (cset == 6) { + for (i = 0; i < WE_6_LEN; i++) if (*buf == we_6[i].nr) break; /* When found, give appropriate msg */ - if (i != we_6_len) { + if (i != WE_6_LEN) { dp += sprintf(dp, " %s\n", we_6[i].descr); dp += we_6[i].f(dp, buf); } else - dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]); + dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]); } else - dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]); + dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]); /* Skip to next element */ if (cs_fest == 8) { - cs = cs_old; + cset = cs_old; cs_old = 0; cs_fest = 0; } @@ -1213,6 +1231,6 @@ } else { dp += sprintf(dp, "Unknown protocol %x!", buf[0]); } - dp += sprintf(dp, "\n"); - HiSax_putstatus(sp, sp->dlogspace); + *dp = 0; + HiSax_putstatus(cs, NULL, cs->dlog); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/s0box.c linux/drivers/isdn/hisax/s0box.c --- v2.0.35/linux/drivers/isdn/hisax/s0box.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/s0box.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,277 @@ +/* $Id: s0box.c,v 1.1.2.1 1998/04/11 18:44:41 keil Exp $ + + * s0box.c low level stuff for Creatix S0BOX + * + * Author S0BOX specific stuff: Enrik Berkhan (enrik@starfleet.inka.de) + * + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +const char *s0box_revision = "$Revision: 1.1.2.1 $"; + +static inline void +writereg(unsigned int padr, signed int addr, u_char off, u_char val) { + unsigned long flags; + + save_flags(flags); + cli(); + outb_p(0x1c,padr+2); + outb_p(0x14,padr+2); + outb_p((addr+off)&0x7f,padr); + outb_p(0x16,padr+2); + outb_p(val,padr); + outb_p(0x17,padr+2); + outb_p(0x14,padr+2); + outb_p(0x1c,padr+2); + restore_flags(flags); +} + +static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ; + +static inline u_char +readreg(unsigned int padr, signed int addr, u_char off) { + register u_char n1, n2; + unsigned long flags; + + save_flags(flags); + cli(); + outb_p(0x1c,padr+2); + outb_p(0x14,padr+2); + outb_p((addr+off)|0x80,padr); + outb_p(0x16,padr+2); + outb_p(0x17,padr+2); + n1 = (inb_p(padr+1) >> 3) & 0x17; + outb_p(0x16,padr+2); + n2 = (inb_p(padr+1) >> 3) & 0x17; + outb_p(0x14,padr+2); + outb_p(0x1c,padr+2); + restore_flags(flags); + return nibtab[n1] | (nibtab[n2] << 4); +} + +static inline void +read_fifo(unsigned int padr, signed int adr, u_char * data, int size) +{ + int i; + register u_char n1, n2; + + outb_p(0x1c, padr+2); + outb_p(0x14, padr+2); + outb_p(adr|0x80, padr); + outb_p(0x16, padr+2); + for (i=0; i> 3) & 0x17; + outb_p(0x16,padr+2); + n2 = (inb_p(padr+1) >> 3) & 0x17; + *(data++)=nibtab[n1] | (nibtab[n2] << 4); + } + outb_p(0x14,padr+2); + outb_p(0x1c,padr+2); + return; +} + +static inline void +write_fifo(unsigned int padr, signed int adr, u_char * data, int size) +{ + int i; + outb_p(0x1c, padr+2); + outb_p(0x14, padr+2); + outb_p(adr&0x7f, padr); + for (i=0; ihw.teles3.cfg_reg, cs->hw.teles3.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset)); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt) + +#include "hscx_irq.c" + +static void +s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ +#define MAXCOUNT 20 + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + int count = 0; + + if (!cs) { + printk(KERN_WARNING "Teles: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + count++; + val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA); + if (val && count < MAXCOUNT) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA); + if (val && count < MAXCOUNT) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (count >= MAXCOUNT) + printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count); + if (stat & 1) { + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); + } + if (stat & 2) { + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0); + } +} + +void +release_io_s0box(struct IsdnCardState *cs) +{ + release_region(cs->hw.teles3.cfg_reg, 8); +} + +static int +S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + break; + case CARD_RELEASE: + release_io_s0box(cs); + break; + case CARD_SETIRQ: + return(request_irq(cs->irq, &s0box_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + break; + case CARD_TEST: + break; + } + return(0); +} + +__initfunc(int +setup_s0box(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, s0box_revision); + printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_S0BOX) + return (0); + + cs->hw.teles3.cfg_reg = card->para[1]; + cs->hw.teles3.hscx[0] = -0x20; + cs->hw.teles3.hscx[1] = 0x0; + cs->hw.teles3.isac = 0x20; + cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e; + cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e; + cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e; + cs->irq = card->para[0]; + if (check_region(cs->hw.teles3.cfg_reg,8)) { + printk(KERN_WARNING + "HiSax: %s ports %x-%x already in use\n", + CardType[cs->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 7); + return 0; + } else + request_region(cs->hw.teles3.cfg_reg, 8, "S0Box parallel I/O"); + printk(KERN_INFO + "HiSax: %s config irq:%d isac:0x%x cfg:0x%x\n", + CardType[cs->typ], cs->irq, + cs->hw.teles3.isac, cs->hw.teles3.cfg_reg); + printk(KERN_INFO + "HiSax: hscx A:0x%x hscx B:0x%x\n", + cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &S0Box_card_msg; + ISACVersion(cs, "S0Box:"); + if (HscxVersion(cs, "S0Box:")) { + printk(KERN_WARNING + "S0Box: wrong HSCX versions check IO address\n"); + release_io_s0box(cs); + return (0); + } + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/sedlbauer.c linux/drivers/isdn/hisax/sedlbauer.c --- v2.0.35/linux/drivers/isdn/hisax/sedlbauer.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/sedlbauer.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,776 @@ +/* $Id: sedlbauer.c,v 1.1.2.16 1998/11/08 13:01:01 niemann Exp $ + + * sedlbauer.c low level stuff for Sedlbauer cards + * includes support for the Sedlbauer speed star (speed star II), + * support for the Sedlbauer speed fax+, + * support for the Sedlbauer ISDN-Controller PC/104 and + * support for the Sedlbauer speed pci + * derived from the original file asuscom.c from Karsten Keil + * + * Copyright (C) 1997,1998 Marcus Niemann (for the modifications to + * the original file asuscom.c) + * + * Author Marcus Niemann (niemann@www-bib.fh-bielefeld.de) + * + * Thanks to Karsten Keil + * Sedlbauer AG for informations + * Edgar Toernig + * + * $Log: sedlbauer.c,v $ + * Revision 1.1.2.16 1998/11/08 13:01:01 niemann + * Added doc for Sedlbauer ISDN cards, + * added info for downloading firmware (Sedlbauer speed fax+) + * + * Revision 1.1.2.15 1998/11/03 00:07:32 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.14 1998/10/30 22:51:41 niemann + * Added new card, Sedlbauer speed pci works now. + * + * Revision 1.1.2.13 1998/10/16 12:46:06 keil + * fix pci detection for more as one card + * + * Revision 1.1.2.12 1998/10/13 18:38:53 keil + * Fix PCI detection + * + * Revision 1.1.2.11 1998/10/13 10:27:30 keil + * New cards, minor fixes + * + * Revision 1.1.2.10 1998/10/11 19:33:52 niemann + * Added new IPAC based cards. + * Code cleanup and simplified (sedlbauer.c) + * + * Revision 1.1.2.9 1998/10/04 23:05:03 keil + * ISAR works now + * + * Revision 1.1.2.8 1998/09/30 22:28:10 keil + * more work for isar support + * + * Revision 1.1.2.7 1998/09/27 13:07:01 keil + * Apply most changes from 2.1.X (HiSax 3.1) + * + * Revision 1.1.2.6 1998/09/12 18:44:06 niemann + * Added new card: Sedlbauer ISDN-Controller PC/104 + * + * Revision 1.1.2.5 1998/04/08 21:58:44 keil + * New init code + * + * Revision 1.1.2.4 1998/02/09 11:21:17 keil + * Sedlbauer PCMCIA support from Marcus Niemann + * + * Revision 1.1.2.3 1998/01/27 22:37:29 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:56 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:10:56 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/11 17:32:04 keil + * new + * + * + */ + +/* Supported cards: + * Card: Chip: Configuration: Comment: + * --------------------------------------------------------------------- + * Speed Card ISAC_HSCX DIP-SWITCH + * Speed Win ISAC_HSCX ISAPNP + * Speed Fax+ ISAC_ISAR ISAPNP #HDLC works# + * Speed Star ISAC_HSCX CARDMGR + * Speed Win2 IPAC ISAPNP + * ISDN PC/104 IPAC DIP-SWITCH + * Speed Star2 IPAC CARDMGR + * Speed PCI IPAC PNP + * + * Important: + * For the sedlbauer speed fax+ to work properly you have to download + * the firmware onto the card. + * For example: hisaxctrl 9 ISAR.BIN +*/ + +#define SEDLBAUER_PCI 1 + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" +#include "isar.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; + +const char *Sedlbauer_revision = "$Revision: 1.1.2.16 $"; + +const char *Sedlbauer_Types[] = + {"None", "speed card/win", "speed star", "speed fax+", + "speed win II / ISDN PC/104", "speed star II", "speed pci"}; + +#ifdef SEDLBAUER_PCI +#define PCI_VENDOR_SEDLBAUER 0xe159 +#define PCI_SPEEDPCI_ID 0x02 +#endif + +#define SEDL_SPEED_CARD_WIN 1 +#define SEDL_SPEED_STAR 2 +#define SEDL_SPEED_FAX 3 +#define SEDL_SPEED_WIN2_PC104 4 +#define SEDL_SPEED_STAR2 5 +#define SEDL_SPEED_PCI 6 + +#define SEDL_CHIP_TEST 0 +#define SEDL_CHIP_ISAC_HSCX 1 +#define SEDL_CHIP_ISAC_ISAR 2 +#define SEDL_CHIP_IPAC 3 + +#define SEDL_BUS_ISA 1 +#define SEDL_BUS_PCI 2 +#define SEDL_BUS_PCMCIA 3 + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define SEDL_HSCX_ISA_RESET_ON 0 +#define SEDL_HSCX_ISA_RESET_OFF 1 +#define SEDL_HSCX_ISA_ISAC 2 +#define SEDL_HSCX_ISA_HSCX 3 +#define SEDL_HSCX_ISA_ADR 4 + +#define SEDL_HSCX_PCMCIA_RESET 0 +#define SEDL_HSCX_PCMCIA_ISAC 1 +#define SEDL_HSCX_PCMCIA_HSCX 2 +#define SEDL_HSCX_PCMCIA_ADR 4 + +#define SEDL_ISAR_ISA_ISAC 4 +#define SEDL_ISAR_ISA_ISAR 6 +#define SEDL_ISAR_ISA_ADR 8 +#define SEDL_ISAR_ISA_ISAR_RESET_ON 10 +#define SEDL_ISAR_ISA_ISAR_RESET_OFF 12 + +#define SEDL_IPAC_ANY_ADR 0 +#define SEDL_IPAC_ANY_IPAC 2 + +#define SEDL_IPAC_PCI_BASE 0 +#define SEDL_IPAC_PCI_ADR 0xc0 +#define SEDL_IPAC_PCI_IPAC 0xc8 + +#define SEDL_RESET 0x3 /* same as DOS driver */ + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size); +} + +static u_char +ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));} + +static void +WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value); +} + +static void +ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size); +} + +static void +WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.sedl.adr, + cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.sedl.adr, + cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value); +} + +/* ISAR access routines + * mode = 0 access with IRQ on + * mode = 1 access with IRQ off + * mode = 2 access with IRQ off and using last offset + */ + +static u_char +ReadISAR(struct IsdnCardState *cs, int mode, u_char offset) +{ + if (mode == 0) + return (readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset)); + else if (mode == 1) + byteout(cs->hw.sedl.adr, offset); + return(bytein(cs->hw.sedl.hscx)); +} + +static void +WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value) +{ + if (mode == 0) + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset, value); + else { + if (mode == 1) + byteout(cs->hw.sedl.adr, offset); + byteout(cs->hw.sedl.hscx, value); + } +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.sedl.adr, \ + cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); + return; + } + + if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) { + /* The card tends to generate interrupts while being removed + causing us to just crash the kernel. bad. */ + printk(KERN_WARNING "Sedlbauer: card not available!\n"); + return; + } + + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 1) { + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0); + } + if (stat & 2) { + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0); + } +} + +static void +sedlbauer_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista, val, icnt = 20; + + if (!cs) { + printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); + return; + } + ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA); +Start_IPAC: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) + hscx_int_main(cs, val); + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA | 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "Sedlbauer IRQ LOOP\n"); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0); +} + +static void +sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + int cnt = 20; + + if (!cs) { + printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n"); + return; + } + + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT); + Start_ISAR: + if (val & ISAR_IRQSTA) + isar_int_main(cs); + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT); + if ((val & ISAR_IRQSTA) && --cnt) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "ISAR IntStat after IntRoutine"); + goto Start_ISAR; + } + val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA); + if (val && --cnt) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (!cnt) + printk(KERN_WARNING "Sedlbauer IRQ LOOP\n"); + + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, ISAR_IRQMSK); +} + +void +release_io_sedlbauer(struct IsdnCardState *cs) +{ + int bytecnt = (cs->subtyp == SEDL_SPEED_FAX) ? 16 : 8; + + if (cs->hw.sedl.bus == SEDL_BUS_PCI) { + bytecnt = 256; + } + if (cs->hw.sedl.cfg_reg) + release_region(cs->hw.sedl.cfg_reg, bytecnt); +} + +static void +reset_sedlbauer(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "Sedlbauer: resetting card\n"); + + if (!((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && + (cs->hw.sedl.chip == SEDL_CHIP_ISAC_HSCX))) { + if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) { + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xc0); + writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_PCFG, 0x12); + restore_flags(flags); + } else { + byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); + } + } +} + +static int +Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_sedlbauer(cs); + return(0); + case CARD_RELEASE: + release_io_sedlbauer(cs); + return(0); + case CARD_SETIRQ: + if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { + return(request_irq(cs->irq, &sedlbauer_interrupt_isar, + I4L_IRQ_FLAG, "HiSax", cs)); + } else if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) { + return(request_irq(cs->irq, &sedlbauer_interrupt_ipac, + I4L_IRQ_FLAG, "HiSax", cs)); + } else { + return(request_irq(cs->irq, &sedlbauer_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + } + case CARD_INIT: + if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { + clear_pending_isac_ints(cs); + writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, + ISAR_IRQBIT, 0); + initisac(cs); + initisar(cs); + /* Reenable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + /* RESET Receiver and Transmitter */ + cs->writeisac(cs, ISAC_CMDR, 0x41); + } else { + inithscxisac(cs, 3); + } + return(0); + case CARD_TEST: + return(0); + case CARD_LOAD_FIRM: + if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { + if (isar_load_firmware(cs, arg)) + return(1); + else + ll_run(cs); + } + return(0); + } + return(0); +} + + +#ifdef SEDLBAUER_PCI +static int pci_index __initdata = 0; +#endif + +__initfunc(int +setup_sedlbauer(struct IsdnCard *card)) +{ + int bytecnt, ver, val; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, Sedlbauer_revision); + printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp)); + + if (cs->typ == ISDN_CTYPE_SEDLBAUER) { + cs->subtyp = SEDL_SPEED_CARD_WIN; + cs->hw.sedl.bus = SEDL_BUS_ISA; + cs->hw.sedl.chip = SEDL_CHIP_TEST; + } else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) { + cs->subtyp = SEDL_SPEED_STAR; + cs->hw.sedl.bus = SEDL_BUS_PCMCIA; + cs->hw.sedl.chip = SEDL_CHIP_TEST; + } else if (cs->typ == ISDN_CTYPE_SEDLBAUER_FAX) { + cs->subtyp = SEDL_SPEED_FAX; + cs->hw.sedl.bus = SEDL_BUS_ISA; + cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR; + } else + return (0); + + bytecnt = 8; + if (card->para[1]) { + cs->hw.sedl.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { + bytecnt = 16; + } + } else { +/* Probe for Sedlbauer speed pci */ +#if SEDLBAUER_PCI +#if CONFIG_PCI + for (; pci_index < 255; pci_index++) { + unsigned char pci_bus, pci_device_fn; + unsigned int ioaddr; + unsigned char irq; + + if (pcibios_find_device (PCI_VENDOR_SEDLBAUER, + PCI_SPEEDPCI_ID, pci_index, + &pci_bus, &pci_device_fn) != 0) { + continue; + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &ioaddr); + cs->irq = irq; + cs->hw.sedl.cfg_reg = ioaddr & PCI_BASE_ADDRESS_IO_MASK; + if (!cs->hw.sedl.cfg_reg) { + printk(KERN_WARNING "Sedlbauer: No IO-Adr for PCI card found\n"); + return(0); + } + cs->hw.sedl.bus = SEDL_BUS_PCI; + cs->hw.sedl.chip = SEDL_CHIP_IPAC; + cs->subtyp = SEDL_SPEED_PCI; + bytecnt = 256; + byteout(cs->hw.sedl.cfg_reg, 0xff); + byteout(cs->hw.sedl.cfg_reg, 0x00); + byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd); + byteout(cs->hw.sedl.cfg_reg+ 5, 0x02); + break; + } + if (pci_index == 255) { + printk(KERN_WARNING "Sedlbauer: No PCI card found\n"); + return(0); + } + pci_index++; +#else + printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n"); + return (0); +#endif /* CONFIG_PCI */ +#endif /* SEDLBAUER_PCI */ + } + + /* In case of the sedlbauer pcmcia card, this region is in use, + reserved for us by the card manager. So we do not check it + here, it would fail. */ + if (cs->hw.sedl.bus != SEDL_BUS_PCMCIA && + check_region((cs->hw.sedl.cfg_reg), bytecnt)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.sedl.cfg_reg, + cs->hw.sedl.cfg_reg + bytecnt); + return (0); + } else { + request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn"); + } + + printk(KERN_INFO + "Sedlbauer: defined at 0x%x-0x%x IRQ %d\n", + cs->hw.sedl.cfg_reg, + cs->hw.sedl.cfg_reg + bytecnt, + cs->irq); + + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Sedl_card_msg; + +/* + * testing ISA and PCMCIA Cards for IPAC, default is ISAC + * do not test for PCI card, because ports are different + * and PCI card uses only IPAC (for the moment) + */ + if (cs->hw.sedl.bus != SEDL_BUS_PCI) { + val = readreg(cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR, + cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC, IPAC_ID); + if (val == 1) { + /* IPAC */ + cs->subtyp = SEDL_SPEED_WIN2_PC104; + if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) { + cs->subtyp = SEDL_SPEED_STAR2; + } + cs->hw.sedl.chip = SEDL_CHIP_IPAC; + } else { + /* ISAC_HSCX oder ISAC_ISAR */ + if (cs->hw.sedl.chip == SEDL_CHIP_TEST) { + cs->hw.sedl.chip = SEDL_CHIP_ISAC_HSCX; + } + } + } + +/* + * hw.sedl.chip is now properly set + */ + printk(KERN_INFO "Sedlbauer: %s detected\n", + Sedlbauer_Types[cs->subtyp]); + + + if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) { + /* IPAC */ + if (cs->hw.sedl.bus == SEDL_BUS_PCI) { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC; + } else { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC; + } + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + cs->readisac = &ReadISAC_IPAC; + cs->writeisac = &WriteISAC_IPAC; + cs->readisacfifo = &ReadISACfifo_IPAC; + cs->writeisacfifo = &WriteISACfifo_IPAC; + + val = readreg(cs->hw.sedl.adr,cs->hw.sedl.isac, IPAC_ID); + printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val); + reset_sedlbauer(cs); + } else { + /* ISAC_HSCX oder ISAC_ISAR */ + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_ON; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_OFF; + cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar; + cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar; + test_and_set_bit(HW_ISAR, &cs->HW_Flags); + + ISACVersion(cs, "Sedlbauer:"); + + cs->BC_Read_Reg = &ReadISAR; + cs->BC_Write_Reg = &WriteISAR; + cs->BC_Send_Data = &isar_fill_fifo; + ver = ISARVersion(cs, "Sedlbauer:"); + if (ver < 0) { + printk(KERN_WARNING + "Sedlbauer: wrong ISAR version (ret = %d)\n", ver); + release_io_sedlbauer(cs); + return (0); + } + } else { + if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_HSCX; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET; + } else { + cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ADR; + cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ISAC; + cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_HSCX; + cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON; + cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF; + } + ISACVersion(cs, "Sedlbauer:"); + + if (HscxVersion(cs, "Sedlbauer:")) { + printk(KERN_WARNING + "Sedlbauer: wrong HSCX versions check IO address\n"); + release_io_sedlbauer(cs); + return (0); + } + reset_sedlbauer(cs); + } + } + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/sportster.c linux/drivers/isdn/hisax/sportster.c --- v2.0.35/linux/drivers/isdn/hisax/sportster.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/sportster.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,286 @@ +/* $Id: sportster.c,v 1.1.2.4 1998/04/08 21:58:46 keil Exp $ + + * sportster.c low level stuff for USR Sportster internal TA + * + * Author Karsten Keil (keil@temic-ech.spacenet.de) + * + * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation + * + * $Log: sportster.c,v $ + * Revision 1.1.2.4 1998/04/08 21:58:46 keil + * New init code + * + * Revision 1.1.2.3 1998/01/27 22:37:31 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:57 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:10:58 keil + * new files on 2.0 + * + */ +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +const char *sportster_revision = "$Revision: 1.1.2.4 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define SPORTSTER_ISAC 0xC000 +#define SPORTSTER_HSCXA 0x0000 +#define SPORTSTER_HSCXB 0x4000 +#define SPORTSTER_RES_IRQ 0x8000 +#define SPORTSTER_RESET 0x80 +#define SPORTSTER_INTE 0x40 + +static inline int +calc_off(unsigned int base, unsigned int off) +{ + return(base + ((off & 0xfc)<<8) + ((off & 3)<<1)); +} + +static inline void +read_fifo(unsigned int adr, u_char * data, int size) +{ + insb(adr, data, size); +} + +static void +write_fifo(unsigned int adr, u_char * data, int size) +{ + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (bytein(calc_off(cs->hw.spt.isac, offset))); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + byteout(calc_off(cs->hw.spt.isac, offset), value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + read_fifo(cs->hw.spt.isac, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + write_fifo(cs->hw.spt.isac, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg)) +#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt) + +#include "hscx_irq.c" + +static void +sportster_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + + if (!cs) { + printk(KERN_WARNING "Sportster: Spurious interrupt!\n"); + return; + } + val = READHSCX(cs, 1, HSCX_ISTA); + Start_HSCX: + if (val) + hscx_int_main(cs, val); + val = ReadISAC(cs, ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = READHSCX(cs, 1, HSCX_ISTA); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = ReadISAC(cs, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + /* get a new irq impulse if there any pending */ + bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1); +} + +void +release_io_sportster(struct IsdnCardState *cs) +{ + int i, adr; + + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0); + for (i=0; i<64; i++) { + adr = cs->hw.spt.cfg_reg + i *1024; + release_region(adr, 8); + } +} + +void +reset_sportster(struct IsdnCardState *cs) +{ + long flags; + + cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_sportster(cs); + return(0); + case CARD_RELEASE: + release_io_sportster(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &sportster_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 1); + cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */ + byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq); + inithscxisac(cs, 2); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +get_io_range(struct IsdnCardState *cs)) +{ + int i, j, adr; + + for (i=0;i<64;i++) { + adr = cs->hw.spt.cfg_reg + i *1024; + if (check_region(adr, 8)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[cs->typ], adr, adr + 8); + break; + } else + request_region(adr, 8, "sportster"); + } + if (i==64) + return(1); + else { + for (j=0; jhw.spt.cfg_reg + j *1024; + release_region(adr, 8); + } + return(0); + } +} + +__initfunc(int +setup_sportster(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, sportster_revision); + printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_SPORTSTER) + return (0); + + cs->hw.spt.cfg_reg = card->para[1]; + cs->irq = card->para[0]; + if (!get_io_range(cs)) + return (0); + cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC; + cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA; + cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB; + + switch(cs->irq) { + case 5: cs->hw.spt.res_irq = 1; + break; + case 7: cs->hw.spt.res_irq = 2; + break; + case 10:cs->hw.spt.res_irq = 3; + break; + case 11:cs->hw.spt.res_irq = 4; + break; + case 12:cs->hw.spt.res_irq = 5; + break; + case 14:cs->hw.spt.res_irq = 6; + break; + case 15:cs->hw.spt.res_irq = 7; + break; + default:release_io_sportster(cs); + printk(KERN_WARNING "Sportster: wrong IRQ\n"); + return(0); + } + reset_sportster(cs); + printk(KERN_INFO + "HiSax: %s config irq:%d cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.spt.cfg_reg); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Sportster_card_msg; + ISACVersion(cs, "Sportster:"); + if (HscxVersion(cs, "Sportster:")) { + printk(KERN_WARNING + "Sportster: wrong HSCX versions check IO address\n"); + release_io_sportster(cs); + return (0); + } + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c --- v2.0.35/linux/drivers/isdn/hisax/tei.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/hisax/tei.c Sun Nov 15 10:32:58 1998 @@ -1,58 +1,124 @@ -/* $Id: tei.c,v 1.8 1997/04/07 22:59:08 keil Exp $ +/* $Id: tei.c,v 1.8.2.7 1998/11/03 00:07:35 keil Exp $ - * Author Karsten Keil (keil@temic-ech.spacenet.de) + * Author Karsten Keil (keil@isdn4linux.de) * based on the teles driver from Jan den Ouden * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/HiSax.cert + * * Thanks to Jan den Ouden * Fritz Elfert * * $Log: tei.c,v $ - * Revision 1.8 1997/04/07 22:59:08 keil - * GFP_KERNEL --> GFP_ATOMIC + * Revision 1.8.2.7 1998/11/03 00:07:35 keil + * certification related changes + * fixed logging for smaller stack use * - * Revision 1.7 1997/04/06 22:54:03 keil - * Using SKB's + * Revision 1.8.2.6 1998/05/27 18:06:21 keil + * HiSax 3.0 + * + * Revision 1.8.2.5 1998/03/07 23:15:38 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.6 1997/02/09 00:25:12 keil - * new interface handling, one interface per card + * Revision 1.8.2.4 1998/01/27 22:43:49 keil + * fixed MDL_ASSIGN_REQ * - * Revision 1.5 1997/01/27 15:57:51 keil + * Revision 1.8.2.3 1997/11/15 18:54:20 keil * cosmetics * - * Revision 1.4 1997/01/21 22:32:44 keil - * Tei verify request + * Revision 1.8.2.2 1997/10/17 22:14:23 keil + * update to last hisax version + * + * Revision 2.2 1997/07/31 19:24:39 keil + * fixed a warning * - * Revision 1.3 1997/01/04 13:45:02 keil - * cleanup,adding remove tei request (thanks to Sim Yskes) + * Revision 2.1 1997/07/31 11:50:16 keil + * ONE TEI and FIXED TEI handling * - * Revision 1.2 1996/12/08 19:52:39 keil - * minor debug fix + * Revision 2.0 1997/07/27 21:13:30 keil + * New TEI managment * - * Revision 1.1 1996/10/13 20:04:57 keil - * Initial revision + * Revision 1.9 1997/06/26 11:18:02 keil + * New managment * + * Revision 1.8 1997/04/07 22:59:08 keil + * GFP_KERNEL --> GFP_ATOMIC * + * Revision 1.7 1997/04/06 22:54:03 keil + * Using SKB's + * + * Old log removed/ KKe * */ #define __NO_VERSION__ #include "hisax.h" +#include "isdnl2.h" +#include -extern struct IsdnCard cards[]; -extern int nrcards; +const char *tei_revision = "$Revision: 1.8.2.7 $"; -const char *tei_revision = "$Revision: 1.8 $"; +#define ID_REQUEST 1 +#define ID_ASSIGNED 2 +#define ID_DENIED 3 +#define ID_CHK_REQ 4 +#define ID_CHK_RES 5 +#define ID_REMOVE 6 +#define ID_VERIFY 7 + +#define TEI_ENTITY_ID 0xf + +static +struct Fsm teifsm = +{NULL, 0, 0, NULL, NULL}; + +void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb); + +enum { + ST_TEI_NOP, + ST_TEI_IDREQ, + ST_TEI_IDVERIFY, +}; + +#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) + +static char *strTeiState[] = +{ + "ST_TEI_NOP", + "ST_TEI_IDREQ", + "ST_TEI_IDVERIFY", +}; + +enum { + EV_IDREQ, + EV_ASSIGN, + EV_DENIED, + EV_CHKREQ, + EV_REMOVE, + EV_VERIFY, + EV_T202, +}; + +#define TEI_EVENT_COUNT (EV_T202+1) + +static char *strTeiEvent[] = +{ + "EV_IDREQ", + "EV_ASSIGN", + "EV_DENIED", + "EV_CHKREQ", + "EV_REMOVE", + "EV_VERIFY", + "EV_T202", +}; -static struct PStack * -findces(struct PStack *st, int ces) +unsigned int +random_ri(void) { - struct PStack *ptr = *(st->l1.stlistp); + unsigned int x; - while (ptr) - if (ptr->l2.ces == ces) - return (ptr); - else - ptr = ptr->next; - return (NULL); + get_random_bytes(&x, sizeof(x)); + return (x & 0xffff); } static struct PStack * @@ -72,246 +138,346 @@ } static void -mdl_unit_data_res(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei) { struct sk_buff *skb; u_char *bp; - if (!(skb = alloc_skb(6 + MAX_HEADER_LEN, GFP_ATOMIC))) { + if (!(skb = alloc_skb(8, GFP_ATOMIC))) { printk(KERN_WARNING "HiSax: No skb for TEI manager\n"); return; } SET_SKB_FREE(skb); - skb_reserve(skb, MAX_HEADER_LEN); + bp = skb_put(skb, 3); + bp[0] = (TEI_SAPI << 2); + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; bp = skb_put(skb, 5); - bp[0] = 0xf; + bp[0] = TEI_ENTITY_ID; bp[1] = ri >> 8; bp[2] = ri & 0xff; - bp[3] = mt; - bp[4] = (ai << 1) | 1; - st->l3.l3l2(st, DL_UNIT_DATA, skb); + bp[3] = m_id; + bp[4] = (tei << 1) | 1; + st->l2.l2l1(st, PH_DATA | REQUEST, skb); } static void -mdl_unit_data_ind(struct PStack *st, unsigned int ri, u_char mt, u_char ai) +tei_id_request(struct FsmInst *fi, int event, void *arg) { - unsigned int tces; - struct PStack *otsp, *ptr; - char tmp[64]; + struct PStack *st = fi->userdata; - switch (mt) { - case (2): - tces = ri; - if (st->l3.debug) { - sprintf(tmp, "identity assign ces %d ai %d", tces, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - if ((otsp = findces(st, tces))) { - if (st->l3.debug) { - sprintf(tmp, "ces %d --> tei %d", tces, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - otsp->ma.teil2(otsp, MDL_ASSIGN, (void *) (int) ai); - } - break; - case (3): - tces = ri; - if (st->l3.debug) { - sprintf(tmp, "identity denied for ces %d ai %d", tces, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - if ((otsp = findces(st, tces))) { - if (st->l3.debug) { - sprintf(tmp, "ces %d denied tei %d", tces, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - otsp->l2.tei = 255; - otsp->l2.ces = randomces(); - otsp->ma.teil2(otsp, MDL_REMOVE, 0); - } - break; - case (4): - if (st->l3.debug) { - sprintf(tmp, "checking identity for %d", ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - if (ai == 0x7f) { - ptr = *(st->l1.stlistp); - while (ptr) { - if ((ptr->l2.tei & 0x7f) != 0x7f) { - if (st->l3.debug) { - sprintf(tmp, "check response for ces %d with tei %d", - ptr->l2.ces, ptr->l2.tei); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - /* send identity check response (user->network) */ - mdl_unit_data_res(st, ptr->l2.ces, 5, ptr->l2.tei); - } - ptr = ptr->next; - } - } else { - otsp = findtei(st, ai); - if (!otsp) - break; - if (st->l3.debug) { - sprintf(tmp, "check response for ces %d with tei %d", - otsp->l2.ces, otsp->l2.tei); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - /* send identity check response (user->network) */ - mdl_unit_data_res(st, otsp->l2.ces, 5, otsp->l2.tei); - } - break; - case (6): - if (st->l3.debug) { - sprintf(tmp, "removal for %d", ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - if (ai == 0x7f) { - ptr = *(st->l1.stlistp); - while (ptr) { - if ((ptr->l2.tei & 0x7f) != 0x7f) { - if (st->l3.debug) { - sprintf(tmp, "rem ces %d with tei %d", - ptr->l2.ces, ptr->l2.tei); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - ptr->ma.teil2(ptr, MDL_REMOVE, 0); - } - ptr = ptr->next; - } - } else { - otsp = findtei(st, ai); - if (!otsp) - break; - if (st->l3.debug) { - sprintf(tmp, "rem ces %d with tei %d", - otsp->l2.ces, otsp->l2.tei); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - otsp->ma.teil2(otsp, MDL_REMOVE, 0); - } - break; - default: - if (st->l3.debug) { - sprintf(tmp, "message unknown %d ai %d", mt, ai); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } + if (st->l2.tei != -1) { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "assign request for allready asigned tei %d", + st->l2.tei); + return; } + st->ma.ri = random_ri(); + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "assign request ri %d", st->ma.ri); + put_tei_msg(st, ID_REQUEST, st->ma.ri, 127); + FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1); + st->ma.N202 = 3; } -void -tei_handler(struct PStack *st, - u_char pr, struct sk_buff *skb) +static void +tei_id_assign(struct FsmInst *fi, int event, void *arg) { - u_char *bp; - unsigned int data; - char tmp[32]; + struct PStack *ost, *st = fi->userdata; + struct sk_buff *skb = arg; + struct IsdnCardState *cs; + int ri, tei; + + 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, + "identity assign ri %d tei %d", ri, tei); + if ((ost = findtei(st, tei))) { /* same tei is in use */ + if (ri != ost->ma.ri) { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "possible duplicate assignment tei %d", tei); + ost->l2.l2tei(ost, MDL_ERROR | RESPONSE, NULL); + } + } else if (ri == st->ma.ri) { + FsmDelTimer(&st->ma.t202, 1); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); + } +} - switch (pr) { - case (MDL_ASSIGN): - data = (unsigned int) skb; - if (st->l3.debug) { - sprintf(tmp, "ces %d assign request", data); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - mdl_unit_data_res(st, data, 1, 127); - break; - case (MDL_VERIFY): - data = (unsigned int) skb; - if (st->l3.debug) { - sprintf(tmp, "%d id verify request", data); - st->l2.l2m.printdebug(&st->l2.l2m, tmp); - } - mdl_unit_data_res(st, 0, 7, data); - break; - case (DL_UNIT_DATA): - bp = skb->data; - if (bp[0] != 0xf) { - /* wrong management entity identifier, ignore */ - /* shouldn't ibh be released??? */ - printk(KERN_WARNING "tei handler wrong entity id %x\n", bp[0]); - } else - mdl_unit_data_ind(st, (bp[1] << 8) | bp[2], bp[3], bp[4] >> 1); - dev_kfree_skb(skb, FREE_READ); - break; - default: - break; +static void +tei_id_denied(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int ri, tei; + + 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, + "identity denied ri %d tei %d", ri, tei); +} + +static void +tei_id_chk_req(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + int tei; + + tei = skb->data[4] >> 1; + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "identity check req tei %d", tei); + if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) { + FsmDelTimer(&st->ma.t202, 4); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + put_tei_msg(st, ID_CHK_RES, random_ri(), st->l2.tei); } } -unsigned int -randomces(void) +static void +tei_id_remove(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct sk_buff *skb = arg; + struct IsdnCardState *cs; + int tei; + + tei = skb->data[4] >> 1; + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "identity remove tei %d", tei); + if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) { + FsmDelTimer(&st->ma.t202, 5); + FsmChangeState(&st->ma.tei_m, ST_TEI_NOP); + st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + } +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) { - int x = jiffies & 0xffff; + struct PStack *st = fi->userdata; - return (x); + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "id verify request for tei %d", st->l2.tei); + put_tei_msg(st, ID_VERIFY, 0, st->l2.tei); + FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2); + st->ma.N202 = 2; } static void -tei_man(struct PStack *sp, int i, void *v) +tei_id_req_tout(struct FsmInst *fi, int event, void *arg) { + struct PStack *st = fi->userdata; + struct IsdnCardState *cs; + + if (--st->ma.N202) { + st->ma.ri = random_ri(); + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "assign req(%d) ri %d", 4 - st->ma.N202, + st->ma.ri); + put_tei_msg(st, ID_REQUEST, st->ma.ri, 127); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3); + } else { + st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed"); + st->l3.l3l2(st, MDL_ERROR | RESPONSE, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + struct PStack *st = fi->userdata; + struct IsdnCardState *cs; + + if (--st->ma.N202) { + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "id verify req(%d) for tei %d", + 3 - st->ma.N202, st->l2.tei); + put_tei_msg(st, ID_VERIFY, 0, st->l2.tei); + FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4); + } else { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "verify req for tei %d failed", st->l2.tei); + st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL); + FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_l1l2(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + int mt; + + if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { + dev_kfree_skb(skb, FREE_READ); + return; + } - printk(KERN_DEBUG "tei_man\n"); + if (pr == (PH_DATA | INDICATION)) { + if (skb->len < 3) { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "short mgr frame %ld/3", skb->len); + } else if (((skb->data[0] >> 2) != TEI_SAPI) || + ((skb->data[1] >> 1) != GROUP_TEI)) { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "wrong mgr sapi/tei %x/%x", + skb->data[0], skb->data[1]); + } else if ((skb->data[2] & 0xef) != UI) { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "mgr frame is not ui %x", skb->data[2]); + } else { + skb_pull(skb, 3); + if (skb->len < 5) { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "short mgr frame %ld/5", skb->len); + } else if (skb->data[0] != TEI_ENTITY_ID) { + /* wrong management entity identifier, ignore */ + st->ma.tei_m.printdebug(&st->ma.tei_m, + "tei handler wrong entity id %x", + skb->data[0]); + } else { + mt = skb->data[3]; + if (mt == ID_ASSIGNED) + FsmEvent(&st->ma.tei_m, EV_ASSIGN, skb); + else if (mt == ID_DENIED) + FsmEvent(&st->ma.tei_m, EV_DENIED, skb); + else if (mt == ID_CHK_REQ) + FsmEvent(&st->ma.tei_m, EV_CHKREQ, skb); + else if (mt == ID_REMOVE) + FsmEvent(&st->ma.tei_m, EV_REMOVE, skb); + else { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "tei handler wrong mt %x\n", mt); + } + } + } + } else { + st->ma.tei_m.printdebug(&st->ma.tei_m, + "tei handler wrong pr %x\n", pr); + } + dev_kfree_skb(skb, FREE_READ); } static void tei_l2tei(struct PStack *st, int pr, void *arg) { - struct IsdnCardState *sp = st->l1.hardware; + struct IsdnCardState *cs; + + if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) { + if (pr == (MDL_ASSIGN | INDICATION)) { + if (st->ma.debug) + st->ma.tei_m.printdebug(&st->ma.tei_m, + "fixed assign tei %d", st->l2.tei); + st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei); + cs = (struct IsdnCardState *) st->l1.hardware; + cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL); + } + return; + } + switch (pr) { + case (MDL_ASSIGN | INDICATION): + FsmEvent(&st->ma.tei_m, EV_IDREQ, arg); + break; + case (MDL_ERROR | REQUEST): + FsmEvent(&st->ma.tei_m, EV_VERIFY, arg); + break; + default: + break; + } +} + +static void +tei_debug(struct FsmInst *fi, char *fmt, ...) +{ + va_list args; + struct PStack *st = fi->userdata; - tei_handler(sp->teistack, pr, arg); + va_start(args, fmt); + VHiSax_putstatus(st->l1.hardware, "tei ", fmt, args); + va_end(args); } void setstack_tei(struct PStack *st) { st->l2.l2tei = tei_l2tei; + st->ma.T202 = 2000; /* T202 2000 milliseconds */ + st->l1.l1tei = tei_l1l2; + st->ma.debug = 1; + st->ma.tei_m.fsm = &teifsm; + st->ma.tei_m.state = ST_TEI_NOP; + st->ma.tei_m.debug = 1; + st->ma.tei_m.userdata = st; + st->ma.tei_m.userint = 0; + st->ma.tei_m.printdebug = tei_debug; + FsmInitTimer(&st->ma.tei_m, &st->ma.t202); } void -init_tei(struct IsdnCardState *sp, int protocol) +init_tei(struct IsdnCardState *cs, int protocol) { - struct PStack *st; - char tmp[128]; - - st = (struct PStack *) kmalloc(sizeof(struct PStack), GFP_ATOMIC); - setstack_HiSax(st, sp); - st->l2.extended = !0; - st->l2.laptype = LAPD; - st->l2.window = 1; - st->l2.orig = !0; - st->protocol = protocol; -/* - * the following is not necessary for tei mng. (broadcast only) - */ - st->l2.t200 = 500; /* 500 milliseconds */ - st->l2.n200 = 4; /* try 4 times */ +} - st->l2.sap = 63; - st->l2.tei = 127; +void +release_tei(struct IsdnCardState *cs) +{ + struct PStack *st = cs->stlist; - sprintf(tmp, "Card %d tei", sp->cardnr + 1); - setstack_isdnl2(st, tmp); - st->l2.debug = 0; - st->l3.debug = 0; - - st->ma.manl2(st, MDL_NOTEIPROC, NULL); - - st->l2.l2l3 = (void *) tei_handler; - st->l1.l1man = tei_man; - st->l2.l2man = tei_man; - st->l4.l2writewakeup = NULL; + while (st) { + FsmDelTimer(&st->ma.t202, 1); + st = st->next; + } +} - HiSax_addlist(sp, st); - sp->teistack = st; +static struct FsmNode TeiFnList[] HISAX_INITDATA = +{ + {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {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}, + {ST_TEI_IDREQ, EV_T202, tei_id_req_tout}, + {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, + {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, + {ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout}, + {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, + {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, +}; + +#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode)) + +HISAX_INITFUNC(void +TeiNew(void)) +{ + teifsm.state_count = TEI_STATE_COUNT; + teifsm.event_count = TEI_EVENT_COUNT; + teifsm.strEvent = strTeiEvent; + teifsm.strState = strTeiState; + FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT); } void -release_tei(struct IsdnCardState *sp) +TeiFree(void) { - struct PStack *st = sp->teistack; - - HiSax_rmlist(sp, st); - kfree((void *) st); + FsmFree(&teifsm); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/teleint.c linux/drivers/isdn/hisax/teleint.c --- v2.0.35/linux/drivers/isdn/hisax/teleint.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/teleint.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,376 @@ +/* $Id: teleint.c,v 1.1.2.7 1998/11/03 00:07:39 keil Exp $ + + * teleint.c low level stuff for TeleInt isdn cards + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: teleint.c,v $ + * Revision 1.1.2.7 1998/11/03 00:07:39 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.6 1998/05/27 18:06:24 keil + * HiSax 3.0 + * + * Revision 1.1.2.5 1998/04/08 21:58:48 keil + * New init code + * + * Revision 1.1.2.4 1998/04/04 21:58:27 keil + * fix HFC BUSY on ISAC fifos + * + * Revision 1.1.2.3 1998/01/27 22:37:41 keil + * fast io + * + * Revision 1.1.2.2 1997/11/15 18:50:58 keil + * new common init function + * + * Revision 1.1.2.1 1997/10/17 22:11:00 keil + * new files on 2.0 + * + * Revision 1.1 1997/09/11 17:32:32 keil + * new + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hfc_2bs0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *TeleInt_revision = "$Revision: 1.1.2.7 $"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + int max_delay = 2000; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = HFC_BUSY & bytein(ale); + while (ret && --max_delay) + ret = HFC_BUSY & bytein(ale); + if (!max_delay) { + printk(KERN_WARNING "TeleInt Busy not inaktive\n"); + restore_flags(flags); + return (0); + } + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + register u_char ret; + register int max_delay = 20000; + register int i; + + byteout(ale, off); + for (i = 0; ihw.hfc.cip = offset; + return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + cs->hw.hfc.cip = offset; + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + cs->hw.hfc.cip = 0; + readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + cs->hw.hfc.cip = 0; + writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size); +} + +static u_char +ReadHFC(struct IsdnCardState *cs, int data, u_char reg) +{ + register u_char ret; + + if (data) { + cs->hw.hfc.cip = reg; + byteout(cs->hw.hfc.addr | 1, reg); + ret = bytein(cs->hw.hfc.addr); + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) + debugl1(cs, "hfc RD %02x %02x", reg, ret); + } else + ret = bytein(cs->hw.hfc.addr | 1); + return (ret); +} + +static void +WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value) +{ + byteout(cs->hw.hfc.addr | 1, reg); + cs->hw.hfc.cip = reg; + if (data) + byteout(cs->hw.hfc.addr, value); + if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) + debugl1(cs, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value); +} + +static void +TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "TeleInt: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (stat & 2) { + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF); + writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0); + } +} + +static void +TeleInt_Timer(struct IsdnCardState *cs) +{ + int stat = 0; + + if (cs->bcs[0].mode) { + stat |= 1; + main_irq_hfc(&cs->bcs[0]); + } + if (cs->bcs[1].mode) { + stat |= 2; + main_irq_hfc(&cs->bcs[1]); + } + cs->hw.hfc.timer.expires = jiffies + 1; + add_timer(&cs->hw.hfc.timer); +} + +void +release_io_TeleInt(struct IsdnCardState *cs) +{ + del_timer(&cs->hw.hfc.timer); + releasehfc(cs); + if (cs->hw.hfc.addr) + release_region(cs->hw.hfc.addr, 2); +} + +static void +reset_TeleInt(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "TeleInt: resetting card\n"); + cs->hw.hfc.cirm |= HFC_RESET; + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 3; + schedule(); + cs->hw.hfc.cirm &= ~HFC_RESET; + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + restore_flags(flags); +} + +static int +TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_TeleInt(cs); + return(0); + case CARD_RELEASE: + release_io_TeleInt(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &TeleInt_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithfc(cs); + clear_pending_isac_ints(cs); + initisac(cs); + /* Reenable all IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + cs->writeisac(cs, ISAC_CMDR, 0x41); + cs->hw.hfc.timer.expires = jiffies + 1; + add_timer(&cs->hw.hfc.timer); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_TeleInt(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, TeleInt_revision); + printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELEINT) + return (0); + + cs->hw.hfc.addr = card->para[1] & 0x3fe; + cs->irq = card->para[0]; + cs->hw.hfc.cirm = HFC_CIRM; + cs->hw.hfc.isac_spcr = 0x00; + cs->hw.hfc.cip = 0; + cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER; + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->hw.hfc.fifosize = 7 * 1024 + 512; + cs->hw.hfc.timer.function = (void *) TeleInt_Timer; + cs->hw.hfc.timer.data = (long) cs; + init_timer(&cs->hw.hfc.timer); + if (check_region((cs->hw.hfc.addr), 2)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.hfc.addr, + cs->hw.hfc.addr + 2); + return (0); + } else { + request_region(cs->hw.hfc.addr, 2, "TeleInt isdn"); + } + /* HW IO = IO */ + byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff); + byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54); + switch (cs->irq) { + case 3: + cs->hw.hfc.cirm |= HFC_INTA; + break; + case 4: + cs->hw.hfc.cirm |= HFC_INTB; + break; + case 5: + cs->hw.hfc.cirm |= HFC_INTC; + break; + case 7: + cs->hw.hfc.cirm |= HFC_INTD; + break; + case 10: + cs->hw.hfc.cirm |= HFC_INTE; + break; + case 11: + cs->hw.hfc.cirm |= HFC_INTF; + break; + default: + printk(KERN_WARNING "TeleInt: wrong IRQ\n"); + release_io_TeleInt(cs); + return (0); + } + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); + byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt); + + printk(KERN_INFO + "TeleInt: defined at 0x%x IRQ %d\n", + cs->hw.hfc.addr, + cs->irq); + + reset_TeleInt(cs); + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHFC; + cs->BC_Write_Reg = &WriteHFC; + cs->cardmsg = &TeleInt_card_msg; + ISACVersion(cs, "TeleInt:"); + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c --- v2.0.35/linux/drivers/isdn/hisax/teles0.c Tue Aug 5 09:00:12 1997 +++ linux/drivers/isdn/hisax/teles0.c Sun Nov 15 10:32:58 1998 @@ -1,4 +1,4 @@ -/* $Id: teles0.c,v 1.8 1997/04/13 19:54:04 keil Exp $ +/* $Id: teles0.c,v 1.8.2.9 1998/04/08 21:58:49 keil Exp $ * teles0.c low level stuff for Teles Memory IO isdn cards * based on the teles driver from Jan den Ouden @@ -10,97 +10,103 @@ * Beat Doebeli * * $Log: teles0.c,v $ - * Revision 1.8 1997/04/13 19:54:04 keil - * Change in IRQ check delay for SMP + * Revision 1.8.2.9 1998/04/08 21:58:49 keil + * New init code * - * Revision 1.7 1997/04/06 22:54:04 keil - * Using SKB's + * Revision 1.8.2.8 1998/03/07 23:15:40 tsbogend + * made HiSax working on Linux/Alpha * - * Revision 1.6 1997/01/27 15:52:18 keil - * SMP proof,cosmetics + * Revision 1.8.2.7 1998/02/03 23:17:16 keil + * IRQ 9 * - * Revision 1.5 1997/01/21 22:25:59 keil - * cleanups + * Revision 1.8.2.6 1998/01/27 22:37:43 keil + * fast io * - * Revision 1.4 1996/11/05 19:41:27 keil - * more changes for 2.1 + * Revision 1.8.2.5 1997/11/15 18:51:00 keil + * new common init function * - * Revision 1.3 1996/10/30 10:22:58 keil - * Changes for 2.1 kernels + * Revision 1.8.2.4 1997/10/17 22:14:26 keil + * update to last hisax version * - * Revision 1.2 1996/10/27 22:08:34 keil - * cosmetic changes + * Revision 2.1 1997/07/27 21:47:10 keil + * new interface structures * - * Revision 1.1 1996/10/13 20:04:58 keil - * Initial revision + * Revision 2.0 1997/06/26 11:02:43 keil + * New Layer and card interface * + * Revision 1.8 1997/04/13 19:54:04 keil + * Change in IRQ check delay for SMP * + * Revision 1.7 1997/04/06 22:54:04 keil + * Using SKB's + * + * removed old log info /KKe * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles0.h" #include "isdnl1.h" -#include +#include "isac.h" +#include "hscx.h" extern const char *CardType[]; -const char *teles0_revision = "$Revision: 1.8 $"; +const char *teles0_revision = "$Revision: 1.8.2.9 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readisac(unsigned int adr, u_char off) { - return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); + return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off); } static inline void writeisac(unsigned int adr, u_char off, u_char data) { - writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off); + writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb(); } static inline u_char readhscx(unsigned int adr, int hscx, u_char off) { - return readb(adr + (hscx ? 0x1e0 : 0x1a0) + + return readb(adr + (hscx ? 0x1c0 : 0x180) + ((off & 1) ? 0x1ff : 0) + off); } static inline void writehscx(unsigned int adr, int hscx, u_char off, u_char data) { - writeb(data, adr + (hscx ? 0x1e0 : 0x1a0) + - ((off & 1) ? 0x1ff : 0) + off); + writeb(data, adr + (hscx ? 0x1c0 : 0x180) + + ((off & 1) ? 0x1ff : 0) + off); mb(); } static inline void read_fifo_isac(unsigned int adr, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) (adr + 0x100); + register u_char *ad = (u_char *) ((long)adr + 0x100); for (i = 0; i < size; i++) data[i] = readb(ad); } -static void +static inline void write_fifo_isac(unsigned int adr, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) (adr + 0x100); - for (i = 0; i < size; i++) - writeb(data[i], ad); + register u_char *ad = (u_char *) ((long)adr + 0x100); + for (i = 0; i < size; i++) { + writeb(data[i], ad); mb(); + } } static inline void read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) { register int i; - register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180)); + register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180)); for (i = 0; i < size; i++) data[i] = readb(ad); } @@ -109,749 +115,206 @@ write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) { int i; - register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180)); - for (i = 0; i < size; i++) - writeb(data[i], ad); -} -static inline void -waitforCEC(int adr, int hscx) -{ - int to = 50; - - while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; + register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180)); + for (i = 0; i < size; i++) { + writeb(data[i], ad); mb(); } - if (!to) - printk(KERN_WARNING "Teles0: waitforCEC timeout\n"); } +/* Interface functions */ -static inline void -waitforXFW(int adr, int hscx) -{ - int to = 50; - - while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles0: waitforXFW timeout\n"); -} - -static inline void -writehscxCMDR(int adr, int hscx, u_char data) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - long flags; - - save_flags(flags); - cli(); - waitforCEC(adr, hscx); - writehscx(adr, hscx, HSCX_CMDR, data); - restore_flags(flags); + return (readisac(cs->hw.teles0.membase, offset)); } -/* - * fast interrupt here - */ - - static void -hscxreport(struct IsdnCardState *sp, int hscx) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->membase, hscx, HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readhscx(sp->membase, hscx, HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->membase, hscx, HSCX_EXIR)); + writeisac(cs->hw.teles0.membase, offset, value); } -void -teles0_report(struct IsdnCardState *sp) -{ - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readisac(sp->membase, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readisac(sp->membase, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readisac(sp->membase, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); -} - -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_hscx(sp->membase, hsp->hscx, ptr, count); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + read_fifo_isac(cs->hw.teles0.membase, data, size); } static void -hscx_fill_fifo(struct HscxState *hsp) -{ - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->membase, hsp->hscx); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo_hscx(sp->membase, hsp->hscx, ptr, count); - writehscxCMDR(sp->membase, hsp->hscx, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readhscx(sp->membase, hsp->hscx, HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!(r & 0x80)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!(r & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->membase, hsp->hscx, 0x80); - } else { - count = readhscx(sp->membase, hsp->hscx, HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "teles0: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "teles0: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } -} - -/* - * ISAC stuff goes here - */ - -static void -isac_empty_fifo(struct IsdnCardState *sp, int count) -{ - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writeisac(sp->membase, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo_isac(sp->membase, ptr, count); - writeisac(sp->membase, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo_isac(cs->hw.teles0.membase, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo_isac(sp->membase, ptr, count); - writeisac(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readhscx(cs->hw.teles0.membase, hscx, offset)); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writeisac(sp->membase, ISAC_CIX0, (command << 2) | 3); + writehscx(cs->hw.teles0.membase, hscx, offset, value); } -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readisac(sp->membase, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writeisac(sp->membase, ISAC_CMDR, 0x80); - } else { - count = readisac(sp->membase, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "teles0: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readisac(sp->membase, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readisac(sp->membase, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readhscx(sp->membase, 1, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->membase, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readhscx(sp->membase, 0, HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->membase, hsp->hscx, 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readhscx(sp->membase, 0, HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void -telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs) +teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs) { - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; + int count = 0; - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Teles0: Spurious interrupt!\n"); return; } - val = readhscx(sp->membase, 1, HSCX_ISTA); + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = readisac(sp->membase, ISAC_ISTA); + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } - val = readhscx(sp->membase, 1, HSCX_ISTA); - if (val) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + count++; + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); + if (val && count < 20) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = readisac(sp->membase, ISAC_ISTA); - if (val) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); + if (val && count < 20) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (stat & 1) { - writehscx(sp->membase, 0, HSCX_MASK, 0xFF); - writehscx(sp->membase, 1, HSCX_MASK, 0xFF); - writehscx(sp->membase, 0, HSCX_MASK, 0x0); - writehscx(sp->membase, 1, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); } if (stat & 2) { - writeisac(sp->membase, ISAC_MASK, 0xFF); - writeisac(sp->membase, ISAC_MASK, 0x0); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); } } - -static void -initisac(struct IsdnCardState *sp) +void +release_io_teles0(struct IsdnCardState *cs) { - unsigned int adr = sp->membase; - - /* 16.0 IOM 1 Mode */ - writeisac(adr, ISAC_MASK, 0xff); - writeisac(adr, ISAC_ADF2, 0x0); - writeisac(adr, ISAC_SPCR, 0xa); - writeisac(adr, ISAC_ADF1, 0x2); - writeisac(adr, ISAC_STCR, 0x70); - writeisac(adr, ISAC_MODE, 0xc9); - writeisac(adr, ISAC_CMDR, 0x41); - writeisac(adr, ISAC_CIX0, (1 << 2) | 3); - writeisac(adr, ISAC_MASK, 0xff); - writeisac(adr, ISAC_MASK, 0x0); + if (cs->hw.teles0.cfg_reg) + release_region(cs->hw.teles0.cfg_reg, 8); } -static void -modehscx(struct HscxState *hs, int mode, int ichan) +static int +reset_teles0(struct IsdnCardState *cs) { - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; + u_char cfval; + long flags; - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writehscx(sp->membase, hscx, HSCX_CCR1, 0x85); - writehscx(sp->membase, hscx, HSCX_XAD1, 0xFF); - writehscx(sp->membase, hscx, HSCX_XAD2, 0xFF); - writehscx(sp->membase, hscx, HSCX_RAH2, 0xFF); - writehscx(sp->membase, hscx, HSCX_XBCH, 0x0); - - /* Switch IOM 1 SSI */ - if (hscx == 0) - ichan = 1 - ichan; - - switch (mode) { - case (0): - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0xff); - writehscx(sp->membase, hscx, HSCX_TSAR, 0xff); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - writehscx(sp->membase, hscx, HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0x7); - writehscx(sp->membase, hscx, HSCX_TSAR, 0x7); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - } else { - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0x3); - writehscx(sp->membase, hscx, HSCX_TSAR, 0x3); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - } - writehscx(sp->membase, hscx, HSCX_MODE, 0xe4); - writehscx(sp->membase, hscx, HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0x7); - writehscx(sp->membase, hscx, HSCX_TSAR, 0x7); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - } else { - writehscx(sp->membase, hscx, HSCX_CCR2, 0x30); - writehscx(sp->membase, hscx, HSCX_TSAX, 0x3); - writehscx(sp->membase, hscx, HSCX_TSAR, 0x3); - writehscx(sp->membase, hscx, HSCX_XCCR, 7); - writehscx(sp->membase, hscx, HSCX_RCCR, 7); - } - writehscx(sp->membase, hscx, HSCX_MODE, 0x8c); - writehscx(sp->membase, hscx, HSCX_CMDR, 0x41); - break; + save_flags(flags); + sti(); + if (cs->hw.teles0.cfg_reg) { + switch (cs->irq) { + case 2: + case 9: + cfval = 0x00; + break; + case 3: + cfval = 0x02; + break; + case 4: + cfval = 0x04; + break; + case 5: + cfval = 0x06; + break; + case 10: + cfval = 0x08; + break; + case 11: + cfval = 0x0A; + break; + case 12: + cfval = 0x0C; + break; + case 15: + cfval = 0x0E; + break; + default: + return(1); + } + cfval |= ((cs->hw.teles0.membase >> 9) & 0xF0); + byteout(cs->hw.teles0.cfg_reg + 4, cfval); + HZDELAY(HZ / 10 + 1); + byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1); + HZDELAY(HZ / 10 + 1); } - writehscx(sp->membase, hscx, HSCX_ISTA, 0x00); -} - -void -release_io_teles0(struct IsdnCard *card) -{ - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 8); + writeb(0, cs->hw.teles0.membase + 0x80); mb(); + HZDELAY(HZ / 5 + 1); + writeb(1, cs->hw.teles0.membase + 0x80); mb(); + HZDELAY(HZ / 5 + 1); + restore_flags(flags); + return(0); } -static void -clear_pending_ints(struct IsdnCardState *sp) +static int +Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) { - int val; - char tmp[64]; - - val = readhscx(sp->membase, 1, HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readhscx(sp->membase, 1, HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readhscx(sp->membase, 0, HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readhscx(sp->membase, 0, HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readhscx(sp->membase, 1, HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readhscx(sp->membase, 0, HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->membase, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readisac(sp->membase, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readisac(sp->membase, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readisac(sp->membase, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readisac(sp->membase, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readisac(sp->membase, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); - } - writeisac(sp->membase, ISAC_MASK, 0); - writeisac(sp->membase, ISAC_CMDR, 0x41); -} - -int -initteles0(struct IsdnCardState *sp) -{ - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat.interrupts[sp->irq]; - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &telesS0_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - sp->modehscx(sp->hs + 1, 0, 0); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat.interrupts[sp->irq] > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d", sp->irq, - kstat.interrupts[sp->irq]); - debugl1(sp, tmp); - if (kstat.interrupts[sp->irq] == sp->counter) { - printk(KERN_WARNING - "Teles0: IRQ(%d) getting no interrupts during init\n", - sp->irq); - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); - } + switch (mt) { + case CARD_RESET: + reset_teles0(cs); + return(0); + case CARD_RELEASE: + release_io_teles0(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &teles0_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); } - return (ret); + return(0); } -int -setup_teles0(struct IsdnCard *card) +__initfunc(int +setup_teles0(struct IsdnCard *card)) { - u_char cfval, val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, teles0_revision); - printk(KERN_NOTICE "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp)); - if ((sp->typ != ISDN_CTYPE_16_0) && (sp->typ != ISDN_CTYPE_8_0)) + printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp)); + if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0)) return (0); - if (sp->typ == ISDN_CTYPE_16_0) - sp->cfg_reg = card->para[2]; + if (cs->typ == ISDN_CTYPE_16_0) + cs->hw.teles0.cfg_reg = card->para[2]; else /* 8.0 */ - sp->cfg_reg = 0; + cs->hw.teles0.cfg_reg = 0; if (card->para[1] < 0x10000) { card->para[1] <<= 4; @@ -859,110 +322,69 @@ "Teles0: membase configured DOSish, assuming 0x%lx\n", (unsigned long) card->para[1]); } - sp->membase = card->para[1]; - sp->irq = card->para[0]; - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 8)) { + cs->hw.teles0.membase = card->para[1]; + cs->irq = card->para[0]; + if (cs->hw.teles0.cfg_reg) { + if (check_region((cs->hw.teles0.cfg_reg), 8)) { printk(KERN_WARNING "HiSax: %s config port %x-%x already in use\n", CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); + cs->hw.teles0.cfg_reg, + cs->hw.teles0.cfg_reg + 8); return (0); } else { - request_region(sp->cfg_reg, 8, "teles cfg"); + request_region(cs->hw.teles0.cfg_reg, 8, "teles cfg"); } } - switch (sp->irq) { - case 2: - cfval = 0x00; - break; - case 3: - cfval = 0x02; - break; - case 4: - cfval = 0x04; - break; - case 5: - cfval = 0x06; - break; - case 10: - cfval = 0x08; - break; - case 11: - cfval = 0x0A; - break; - case 12: - cfval = 0x0C; - break; - case 15: - cfval = 0x0E; - break; - default: - cfval = 0x00; - break; - } - cfval |= ((card->para[1] >> 9) & 0xF0); - if (sp->cfg_reg) { - if ((val = bytein(sp->cfg_reg + 0)) != 0x51) { + if (cs->hw.teles0.cfg_reg) { + if ((val = bytein(cs->hw.teles0.cfg_reg + 0)) != 0x51) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 0, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 0, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - if ((val = bytein(sp->cfg_reg + 1)) != 0x93) { + if ((val = bytein(cs->hw.teles0.cfg_reg + 1)) != 0x93) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 1, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 1, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - val = bytein(sp->cfg_reg + 2); /* 0x1e=without AB - * 0x1f=with AB - * 0x1c 16.3 ??? - */ + val = bytein(cs->hw.teles0.cfg_reg + 2); /* 0x1e=without AB + * 0x1f=with AB + * 0x1c 16.3 ??? + */ if (val != 0x1e && val != 0x1f) { printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n", - sp->cfg_reg + 2, val); - release_region(sp->cfg_reg, 8); + cs->hw.teles0.cfg_reg + 2, val); + release_region(cs->hw.teles0.cfg_reg, 8); return (0); } - save_flags(flags); - byteout(sp->cfg_reg + 4, cfval); - sti(); - HZDELAY(HZ / 10 + 1); - byteout(sp->cfg_reg + 4, cfval | 1); - HZDELAY(HZ / 10 + 1); - restore_flags(flags); } - printk(KERN_NOTICE - "HiSax: %s config irq:%d mem:%x cfg:%x\n", - CardType[sp->typ], sp->irq, - sp->membase, sp->cfg_reg); - verA = readhscx(sp->membase, 0, HSCX_VSTR) & 0xf; - verB = readhscx(sp->membase, 1, HSCX_VSTR) & 0xf; - printk(KERN_INFO "Teles0: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readisac(sp->membase, ISAC_RBCH); - printk(KERN_INFO "Teles0: ISAC %s\n", - ISACVersion(val)); - - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + /* 16.0 and 8.0 designed for IOM1 */ + test_and_set_bit(HW_IOM1, &cs->HW_Flags); + printk(KERN_INFO + "HiSax: %s config irq:%d mem:0x%X cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.teles0.membase, cs->hw.teles0.cfg_reg); + if (reset_teles0(cs)) { + printk(KERN_WARNING "Teles0: wrong IRQ\n"); + release_io_teles0(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Teles_card_msg; + ISACVersion(cs, "Teles0:"); + if (HscxVersion(cs, "Teles0:")) { printk(KERN_WARNING "Teles0: wrong HSCX versions check IO/MEM addresses\n"); - release_io_teles0(card); + release_io_teles0(cs); return (0); } - save_flags(flags); - writeb(0, sp->membase + 0x80); - sti(); - HZDELAY(HZ / 5 + 1); - writeb(1, sp->membase + 0x80); - HZDELAY(HZ / 5 + 1); - restore_flags(flags); - - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c --- v2.0.35/linux/drivers/isdn/hisax/teles3.c Tue Aug 5 09:00:12 1997 +++ linux/drivers/isdn/hisax/teles3.c Sun Nov 15 10:32:58 1998 @@ -1,4 +1,4 @@ -/* $Id: teles3.c,v 1.11 1997/04/13 19:54:05 keil Exp $ +/* $Id: teles3.c,v 1.11.2.9 1998/04/08 21:58:52 keil Exp $ * teles3.c low level stuff for Teles 16.3 & PNP isdn cards * @@ -11,6 +11,30 @@ * Beat Doebeli * * $Log: teles3.c,v $ + * Revision 1.11.2.9 1998/04/08 21:58:52 keil + * New init code + * + * Revision 1.11.2.8 1998/01/27 22:37:46 keil + * fast io + * + * Revision 1.11.2.7 1998/01/11 22:58:01 keil + * make IRQ 9 working again + * + * Revision 1.11.2.6 1997/12/01 22:35:43 keil + * ID Byte for 16.3 version 1.1 + * + * Revision 1.11.2.5 1997/11/15 18:51:03 keil + * new common init function + * + * Revision 1.11.2.4 1997/10/17 22:14:30 keil + * update to last hisax version + * + * Revision 2.1 1997/07/27 21:47:12 keil + * new interface structures + * + * Revision 2.0 1997/06/26 11:02:46 keil + * New Layer and card interface + * * Revision 1.11 1997/04/13 19:54:05 keil * Change in IRQ check delay for SMP * @@ -35,36 +59,20 @@ * Revision 1.6 1997/01/27 15:52:55 keil * SMP proof,cosmetics, PCMCIA added * - * Revision 1.5 1997/01/21 22:28:32 keil - * cleanups - * - * Revision 1.4 1996/12/14 21:05:41 keil - * Reset for 16.3 PnP - * - * Revision 1.3 1996/11/05 19:56:54 keil - * debug output fixed - * - * Revision 1.2 1996/10/27 22:09:15 keil - * cosmetic changes - * - * Revision 1.1 1996/10/13 20:04:59 keil - * Initial revision - * - * + * removed old log info /KKe * */ #define __NO_VERSION__ -#include "siemens.h" #include "hisax.h" -#include "teles3.h" +#include "isac.h" +#include "hscx.h" #include "isdnl1.h" -#include extern const char *CardType[]; -const char *teles3_revision = "$Revision: 1.11 $"; +const char *teles3_revision = "$Revision: 1.11.2.9 $"; -#define byteout(addr,val) outb_p(val,addr) -#define bytein(addr) inb_p(addr) +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) static inline u_char readreg(unsigned int adr, u_char off) @@ -82,953 +90,407 @@ static inline void read_fifo(unsigned int adr, u_char * data, int size) { - insb(adr + 0x1e, data, size); + insb(adr, data, size); } static void write_fifo(unsigned int adr, u_char * data, int size) { - outsb(adr + 0x1e, data, size); -} - -static inline void -waitforCEC(int adr) -{ - int to = 50; - - while ((readreg(adr, HSCX_STAR) & 0x04) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles3: waitforCEC timeout\n"); -} - - -static inline void -waitforXFW(int adr) -{ - int to = 50; - - while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) { - udelay(1); - to--; - } - if (!to) - printk(KERN_WARNING "Teles3: waitforXFW timeout\n"); + outsb(adr, data, size); } -static inline void -writehscxCMDR(int adr, u_char data) -{ - long flags; - save_flags(flags); - cli(); - waitforCEC(adr); - writereg(adr, HSCX_CMDR, data); - restore_flags(flags); -} +/* Interface functions */ -/* - * fast interrupt here - */ - -static void -hscxreport(struct IsdnCardState *sp, int hscx) +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) { - printk(KERN_DEBUG "HSCX %d\n", hscx); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR)); + return (readreg(cs->hw.teles3.isac, offset)); } -void -teles3_report(struct IsdnCardState *sp) -{ - printk(KERN_DEBUG "ISAC\n"); - printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA)); - printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR)); - printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR)); - hscxreport(sp, 0); - hscxreport(sp, 1); -} - -/* - * HSCX stuff goes here - */ - static void -hscx_empty_fifo(struct HscxState *hsp, int count) +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) { - u_char *ptr; - struct IsdnCardState *sp = hsp->sp; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_empty_fifo"); - - if (hsp->rcvidx + count > HSCX_BUFMAX) { - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "hscx_empty_fifo: incoming packet too large"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - hsp->rcvidx = 0; - return; - } - ptr = hsp->rcvbuf + hsp->rcvidx; - hsp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_empty_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + writereg(cs->hw.teles3.isac, offset, value); } static void -hscx_fill_fifo(struct HscxState *hsp) -{ - struct IsdnCardState *sp = hsp->sp; - int more, count; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO)) - debugl1(sp, "hscx_fill_fifo"); - - if (!hsp->tx_skb) - return; - if (hsp->tx_skb->len <= 0) - return; - - more = (hsp->mode == 1) ? 1 : 0; - if (hsp->tx_skb->len > 32) { - more = !0; - count = 32; - } else - count = hsp->tx_skb->len; - - waitforXFW(sp->hscx[hsp->hscx]); - save_flags(flags); - cli(); - ptr = hsp->tx_skb->data; - skb_pull(hsp->tx_skb, count); - hsp->tx_cnt -= count; - hsp->count += count; - write_fifo(sp->hscx[hsp->hscx], ptr, count); - writehscxCMDR(sp->hscx[hsp->hscx], more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_HSCX_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "hscx_fill_fifo %c cnt %d", - hsp->hscx ? 'B' : 'A', count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } -} - -static inline void -hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx) +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char r; - struct HscxState *hsp = sp->hs + hscx; - struct sk_buff *skb; - int count; - char tmp[32]; - - if (!hsp->init) - return; - - if (val & 0x80) { /* RME */ - - r = readreg(sp->hscx[hsp->hscx], HSCX_RSTA); - if ((r & 0xf0) != 0xa0) { - if (!(r & 0x80)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX invalid frame"); - if ((r & 0x40) && hsp->mode) - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX RDO mode=%d", - hsp->mode); - debugl1(sp, tmp); - } - if (!(r & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "HSCX CRC error"); - writehscxCMDR(sp->hscx[hsp->hscx], 0x80); - } else { - count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f; - if (count == 0) - count = 32; - hscx_empty_fifo(hsp, count); - if ((count = hsp->rcvidx - 1) > 0) { - if (!(skb = dev_alloc_skb(count))) - printk(KERN_WARNING "teles3: receive out of memory\n"); - else { - memcpy(skb_put(skb, count), hsp->rcvbuf, count); - skb_queue_tail(&hsp->rqueue, skb); - } - } - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - hscx_empty_fifo(hsp, 32); - if (hsp->mode == 1) { - /* receive audio data */ - if (!(skb = dev_alloc_skb(32))) - printk(KERN_WARNING "teles3: receive out of memory\n"); - else { - memcpy(skb_put(skb, 32), hsp->rcvbuf, 32); - skb_queue_tail(&hsp->rqueue, skb); - } - hsp->rcvidx = 0; - hscx_sched_event(hsp, HSCX_RCVBUFREADY); - } - } - if (val & 0x10) { /* XPR */ - if (hsp->tx_skb) - if (hsp->tx_skb->len) { - hscx_fill_fifo(hsp); - return; - } else { - dev_kfree_skb(hsp->tx_skb, FREE_WRITE); - hsp->count = 0; - if (hsp->st->l4.l1writewakeup) - hsp->st->l4.l1writewakeup(hsp->st); - hsp->tx_skb = NULL; - } - if ((hsp->tx_skb = skb_dequeue(&hsp->squeue))) { - hsp->count = 0; - hscx_fill_fifo(hsp); - } else - hscx_sched_event(hsp, HSCX_XMTBUFREADY); - } + read_fifo(cs->hw.teles3.isacfifo, data, size); } -/* - * ISAC stuff goes here - */ - static void -isac_empty_fifo(struct IsdnCardState *sp, int count) +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) { - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "isac_empty_fifo"); - - if ((sp->rcvidx + count) >= MAX_DFRAME_LEN) { - if (sp->debug & L1_DEB_WARN) { - char tmp[40]; - sprintf(tmp, "isac_empty_fifo overrun %d", - sp->rcvidx + count); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CMDR, 0x80); - sp->rcvidx = 0; - return; - } - ptr = sp->rcvbuf + sp->rcvidx; - sp->rcvidx += count; - save_flags(flags); - cli(); - read_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, 0x80); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_empty_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + write_fifo(cs->hw.teles3.isacfifo, data, size); } -static void -isac_fill_fifo(struct IsdnCardState *sp) +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) { - int count, more; - u_char *ptr; - long flags; - - if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO)) - debugl1(sp, "isac_fill_fifo"); - - if (!sp->tx_skb) - return; - - count = sp->tx_skb->len; - if (count <= 0) - return; - - more = 0; - if (count > 32) { - more = !0; - count = 32; - } - save_flags(flags); - cli(); - ptr = sp->tx_skb->data; - skb_pull(sp->tx_skb, count); - sp->tx_cnt += count; - write_fifo(sp->isac, ptr, count); - writereg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa); - restore_flags(flags); - if (sp->debug & L1_DEB_ISAC_FIFO) { - char tmp[128]; - char *t = tmp; - - t += sprintf(t, "isac_fill_fifo cnt %d", count); - QuickHex(t, ptr, count); - debugl1(sp, tmp); - } + return (readreg(cs->hw.teles3.hscx[hscx], offset)); } static void -ph_command(struct IsdnCardState *sp, unsigned int command) +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) { - if (sp->debug & L1_DEB_ISAC) { - char tmp[32]; - sprintf(tmp, "ph_command %d", command); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_CIX0, (command << 2) | 3); + writereg(cs->hw.teles3.hscx[hscx], offset, value); } +/* + * fast interrupt HSCX stuff goes here + */ -static inline void -isac_interrupt(struct IsdnCardState *sp, u_char val) -{ - u_char exval; - struct sk_buff *skb; - unsigned int count; - char tmp[32]; - - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "ISAC interrupt %x", val); - debugl1(sp, tmp); - } - if (val & 0x80) { /* RME */ - exval = readreg(sp->isac, ISAC_RSTA); - if ((exval & 0x70) != 0x20) { - if (exval & 0x40) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RDO"); - if (!(exval & 0x20)) - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC CRC error"); - writereg(sp->isac, ISAC_CMDR, 0x80); - } else { - count = readreg(sp->isac, ISAC_RBCL) & 0x1f; - if (count == 0) - count = 32; - isac_empty_fifo(sp, count); - if ((count = sp->rcvidx) > 0) { - if (!(skb = alloc_skb(count, GFP_ATOMIC))) - printk(KERN_WARNING "AVM: D receive out of memory\n"); - else { - SET_SKB_FREE(skb); - memcpy(skb_put(skb, count), sp->rcvbuf, count); - skb_queue_tail(&sp->rq, skb); - } - } - } - sp->rcvidx = 0; - isac_sched_event(sp, ISAC_RCVBUFREADY); - } - if (val & 0x40) { /* RPF */ - isac_empty_fifo(sp, 32); - } - if (val & 0x20) { /* RSC */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC RSC interrupt"); - } - if (val & 0x10) { /* XPR */ - if (sp->tx_skb) - if (sp->tx_skb->len) { - isac_fill_fifo(sp); - goto afterXPR; - } else { - dev_kfree_skb(sp->tx_skb, FREE_WRITE); - sp->tx_cnt = 0; - sp->tx_skb = NULL; - } - if ((sp->tx_skb = skb_dequeue(&sp->sq))) { - sp->tx_cnt = 0; - isac_fill_fifo(sp); - } else - isac_sched_event(sp, ISAC_XMTBUFREADY); - } - afterXPR: - if (val & 0x04) { /* CISQ */ - sp->ph_state = (readreg(sp->isac, ISAC_CIX0) >> 2) - & 0xf; - if (sp->debug & L1_DEB_ISAC) { - sprintf(tmp, "l1state %d", sp->ph_state); - debugl1(sp, tmp); - } - isac_new_ph(sp); - } - if (val & 0x02) { /* SIN */ - /* never */ - if (sp->debug & L1_DEB_WARN) - debugl1(sp, "ISAC SIN interrupt"); - } - if (val & 0x01) { /* EXI */ - exval = readreg(sp->isac, ISAC_EXIR); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "ISAC EXIR %02x", exval); - debugl1(sp, tmp); - } - } -} - -static inline void -hscx_int_main(struct IsdnCardState *sp, u_char val) -{ +#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt) - u_char exval; - struct HscxState *hsp; - char tmp[32]; - - - if (val & 0x01) { - hsp = sp->hs + 1; - exval = readreg(sp->hscx[1], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX B EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0xf8) { - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX B interrupt %x", val); - debugl1(sp, tmp); - } - hscx_interrupt(sp, val, 1); - } - if (val & 0x02) { - hsp = sp->hs; - exval = readreg(sp->hscx[0], HSCX_EXIR); - if (exval == 0x40) { - if (hsp->mode == 1) - hscx_fill_fifo(hsp); - else { - /* Here we lost an TX interrupt, so - * restart transmitting the whole frame. - */ - if (hsp->tx_skb) { - skb_push(hsp->tx_skb, hsp->count); - hsp->tx_cnt += hsp->count; - hsp->count = 0; - } - writehscxCMDR(sp->hscx[hsp->hscx], 0x01); - if (sp->debug & L1_DEB_WARN) { - sprintf(tmp, "HSCX A EXIR %x Lost TX", exval); - debugl1(sp, tmp); - } - } - } else if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A EXIR %x", exval); - debugl1(sp, tmp); - } - } - if (val & 0x04) { - exval = readreg(sp->hscx[0], HSCX_ISTA); - if (sp->debug & L1_DEB_HSCX) { - sprintf(tmp, "HSCX A interrupt %x", exval); - debugl1(sp, tmp); - } - hscx_interrupt(sp, exval, 0); - } -} +#include "hscx_irq.c" static void teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs) { #define MAXCOUNT 20 - struct IsdnCardState *sp; + struct IsdnCardState *cs = dev_id; u_char val, stat = 0; int count = 0; - sp = (struct IsdnCardState *) irq2dev_map[intno]; - - if (!sp) { + if (!cs) { printk(KERN_WARNING "Teles: Spurious interrupt!\n"); return; } - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); Start_HSCX: if (val) { - hscx_int_main(sp, val); + hscx_int_main(cs, val); stat |= 1; } - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); Start_ISAC: if (val) { - isac_interrupt(sp, val); + isac_interrupt(cs, val); stat |= 2; } count++; - val = readreg(sp->hscx[1], HSCX_ISTA); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); if (val && count < MAXCOUNT) { - if (sp->debug & L1_DEB_HSCX) - debugl1(sp, "HSCX IntStat after IntRoutine"); + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); goto Start_HSCX; } - val = readreg(sp->isac, ISAC_ISTA); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); if (val && count < MAXCOUNT) { - if (sp->debug & L1_DEB_ISAC) - debugl1(sp, "ISAC IntStat after IntRoutine"); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); goto Start_ISAC; } if (count >= MAXCOUNT) printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count); if (stat & 1) { - writereg(sp->hscx[0], HSCX_MASK, 0xFF); - writereg(sp->hscx[1], HSCX_MASK, 0xFF); - writereg(sp->hscx[0], HSCX_MASK, 0x0); - writereg(sp->hscx[1], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); } if (stat & 2) { - writereg(sp->isac, ISAC_MASK, 0xFF); - writereg(sp->isac, ISAC_MASK, 0x0); - } -} - - -static void -initisac(struct IsdnCardState *sp) -{ - unsigned int adr = sp->isac; - - /* 16.3 IOM 2 Mode */ - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_ADF2, 0x80); - writereg(adr, ISAC_SQXR, 0x2f); - writereg(adr, ISAC_SPCR, 0x0); - writereg(adr, ISAC_ADF1, 0x2); - writereg(adr, ISAC_STCR, 0x70); - writereg(adr, ISAC_MODE, 0xc9); - writereg(adr, ISAC_TIMR, 0x0); - writereg(adr, ISAC_ADF1, 0x0); - writereg(adr, ISAC_CMDR, 0x41); - writereg(adr, ISAC_CIX0, (1 << 2) | 3); - writereg(adr, ISAC_MASK, 0xff); - writereg(adr, ISAC_MASK, 0x0); -} - -static void -modehscx(struct HscxState *hs, int mode, int ichan) -{ - struct IsdnCardState *sp = hs->sp; - int hscx = hs->hscx; - - if (sp->debug & L1_DEB_HSCX) { - char tmp[40]; - sprintf(tmp, "hscx %c mode %d ichan %d", - 'A' + hscx, mode, ichan); - debugl1(sp, tmp); - } - hs->mode = mode; - writereg(sp->hscx[hscx], HSCX_CCR1, 0x85); - writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF); - writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF); - writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF); - writereg(sp->hscx[hscx], HSCX_XBCH, 0x0); - writereg(sp->hscx[hscx], HSCX_RLCR, 0x0); - - switch (mode) { - case (0): - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0xff); - writereg(sp->hscx[hscx], HSCX_TSAR, 0xff); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - writereg(sp->hscx[hscx], HSCX_MODE, 0x84); - break; - case (1): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0xe4); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; - case (2): - if (ichan == 0) { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } else { - writereg(sp->hscx[hscx], HSCX_CCR2, 0x30); - writereg(sp->hscx[hscx], HSCX_TSAX, 0x3); - writereg(sp->hscx[hscx], HSCX_TSAR, 0x3); - writereg(sp->hscx[hscx], HSCX_XCCR, 7); - writereg(sp->hscx[hscx], HSCX_RCCR, 7); - } - writereg(sp->hscx[hscx], HSCX_MODE, 0x8c); - writereg(sp->hscx[hscx], HSCX_CMDR, 0x41); - break; + writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0); } - writereg(sp->hscx[hscx], HSCX_ISTA, 0x00); } inline static void -release_ioregs(struct IsdnCard *card, int mask) +release_ioregs(struct IsdnCardState *cs, int mask) { if (mask & 1) - release_region(card->sp->isac, 32); + release_region(cs->hw.teles3.isac + 32, 32); if (mask & 2) - release_region(card->sp->hscx[0], 32); + release_region(cs->hw.teles3.hscx[0] + 32, 32); if (mask & 4) - release_region(card->sp->hscx[1], 32); + release_region(cs->hw.teles3.hscx[1] + 32, 32); } void -release_io_teles3(struct IsdnCard *card) +release_io_teles3(struct IsdnCardState *cs) { - if (card->sp->typ == ISDN_CTYPE_TELESPCMCIA) - release_region(card->sp->hscx[0], 97); + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) + release_region(cs->hw.teles3.cfg_reg, 97); else { - if (card->sp->cfg_reg) - release_region(card->sp->cfg_reg, 8); - release_ioregs(card, 0x7); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 0x7); } } -static void -clear_pending_ints(struct IsdnCardState *sp) +static int +reset_teles3(struct IsdnCardState *cs) { - int val; - char tmp[64]; - - val = readreg(sp->hscx[1], HSCX_ISTA); - sprintf(tmp, "HSCX B ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->hscx[1], HSCX_EXIR); - sprintf(tmp, "HSCX B EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x02) { - val = readreg(sp->hscx[0], HSCX_EXIR); - sprintf(tmp, "HSCX A EXIR %x", val); - debugl1(sp, tmp); - } - val = readreg(sp->hscx[0], HSCX_ISTA); - sprintf(tmp, "HSCX A ISTA %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[1], HSCX_STAR); - sprintf(tmp, "HSCX B STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->hscx[0], HSCX_STAR); - sprintf(tmp, "HSCX A STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_STAR); - sprintf(tmp, "ISAC STAR %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_MODE); - sprintf(tmp, "ISAC MODE %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ADF2); - sprintf(tmp, "ISAC ADF2 %x", val); - debugl1(sp, tmp); - val = readreg(sp->isac, ISAC_ISTA); - sprintf(tmp, "ISAC ISTA %x", val); - debugl1(sp, tmp); - if (val & 0x01) { - val = readreg(sp->isac, ISAC_EXIR); - sprintf(tmp, "ISAC EXIR %x", val); - debugl1(sp, tmp); - } else if (val & 0x04) { - val = readreg(sp->isac, ISAC_CIR0); - sprintf(tmp, "ISAC CIR0 %x", val); - debugl1(sp, tmp); - } - writereg(sp->isac, ISAC_MASK, 0); - writereg(sp->isac, ISAC_CMDR, 0x41); -} + long flags; + u_char irqcfg; -int -initteles3(struct IsdnCardState *sp) -{ - int ret; - int loop = 0; - char tmp[40]; - - sp->counter = kstat.interrupts[sp->irq]; - sprintf(tmp, "IRQ %d count %d", sp->irq, sp->counter); - debugl1(sp, tmp); - clear_pending_ints(sp); - ret = get_irq(sp->cardnr, &teles3_interrupt); - if (ret) { - initisac(sp); - sp->modehscx(sp->hs, 0, 0); - writereg(sp->hscx[sp->hs->hscx], HSCX_CMDR, 0x01); - sp->modehscx(sp->hs + 1, 0, 0); - writereg(sp->hscx[(sp->hs + 1)->hscx], HSCX_CMDR, 0x01); - while (loop++ < 10) { - /* At least 1-3 irqs must happen - * (one from HSCX A, one from HSCX B, 3rd from ISAC) - */ - if (kstat.interrupts[sp->irq] > sp->counter) - break; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 1; - schedule(); - } - sprintf(tmp, "IRQ %d count %d loop %d", sp->irq, - kstat.interrupts[sp->irq], loop); - debugl1(sp, tmp); - if (kstat.interrupts[sp->irq] <= sp->counter) { - printk(KERN_WARNING - "Teles3: IRQ(%d) getting no interrupts during init\n", - sp->irq); - irq2dev_map[sp->irq] = NULL; - free_irq(sp->irq, NULL); - return (0); - } + if (cs->typ != ISDN_CTYPE_TELESPCMCIA) { + if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) { + switch (cs->irq) { + case 2: + case 9: + irqcfg = 0x00; + break; + case 3: + irqcfg = 0x02; + break; + case 4: + irqcfg = 0x04; + break; + case 5: + irqcfg = 0x06; + break; + case 10: + irqcfg = 0x08; + break; + case 11: + irqcfg = 0x0A; + break; + case 12: + irqcfg = 0x0C; + break; + case 15: + irqcfg = 0x0E; + break; + default: + return(1); + } + save_flags(flags); + byteout(cs->hw.teles3.cfg_reg + 4, irqcfg); + sti(); + HZDELAY(HZ / 10 + 1); + byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1); + HZDELAY(HZ / 10 + 1); + restore_flags(flags); + } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + save_flags(flags); + byteout(cs->hw.teles3.cfg_reg, 0xff); + HZDELAY(2); + byteout(cs->hw.teles3.cfg_reg, 0x00); + HZDELAY(2); + restore_flags(flags); + } else { + /* Reset off for 16.3 PnP , thanks to Georg Acher */ + save_flags(flags); + byteout(cs->hw.teles3.isac + 0x3c, 0); + HZDELAY(2); + byteout(cs->hw.teles3.isac + 0x3c, 1); + HZDELAY(2); + restore_flags(flags); + } + } + return(0); +} + +static int +Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_teles3(cs); + return(0); + case CARD_RELEASE: + release_io_teles3(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &teles3_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); } - return (ret); + return(0); } -int -setup_teles3(struct IsdnCard *card) +__initfunc(int +setup_teles3(struct IsdnCard *card)) { - u_char cfval = 0, val, verA, verB; - struct IsdnCardState *sp = card->sp; - long flags; + u_char val; + struct IsdnCardState *cs = card->cs; char tmp[64]; strcpy(tmp, teles3_revision); - printk(KERN_NOTICE "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp)); - if ((sp->typ != ISDN_CTYPE_16_3) && (sp->typ != ISDN_CTYPE_PNP) - && (sp->typ != ISDN_CTYPE_TELESPCMCIA)) + printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp)); + if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP) + && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) return (0); - if (sp->typ == ISDN_CTYPE_16_3) { - sp->cfg_reg = card->para[1]; - switch (sp->cfg_reg) { + if (cs->typ == ISDN_CTYPE_16_3) { + cs->hw.teles3.cfg_reg = card->para[1]; + switch (cs->hw.teles3.cfg_reg) { case 0x180: case 0x280: case 0x380: - sp->cfg_reg |= 0xc00; + cs->hw.teles3.cfg_reg |= 0xc00; break; } - sp->isac = sp->cfg_reg - 0x400; - sp->hscx[0] = sp->cfg_reg - 0xc00; - sp->hscx[1] = sp->cfg_reg - 0x800; - } else if (sp->typ == ISDN_CTYPE_TELESPCMCIA) { - sp->cfg_reg = 0; - sp->hscx[0] = card->para[1]; - sp->hscx[1] = card->para[1] + 0x20; - sp->isac = card->para[1] + 0x40; - } else { /* PNP */ - sp->cfg_reg = 0; - sp->isac = card->para[1]; - sp->hscx[0] = card->para[2]; - sp->hscx[1] = card->para[2] + 0x20; - } - sp->irq = card->para[0]; - if (sp->typ == ISDN_CTYPE_TELESPCMCIA) { - if (check_region((sp->hscx[0]), 97)) { + cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420; + cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20; + cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820; + } else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { + cs->hw.teles3.cfg_reg = card->para[1]; + cs->hw.teles3.hscx[0] = card->para[1] - 0x20; + cs->hw.teles3.hscx[1] = card->para[1]; + cs->hw.teles3.isac = card->para[1] + 0x20; + } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + cs->hw.teles3.cfg_reg = card->para[3]; + cs->hw.teles3.isac = card->para[2] - 32; + cs->hw.teles3.hscx[0] = card->para[1] - 32; + cs->hw.teles3.hscx[1] = card->para[1]; + } else { /* PNP */ + cs->hw.teles3.cfg_reg = 0; + cs->hw.teles3.isac = card->para[1] - 32; + cs->hw.teles3.hscx[0] = card->para[2] - 32; + cs->hw.teles3.hscx[1] = card->para[2]; + } + cs->irq = card->para[0]; + cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e; + cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e; + cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e; + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { + if (check_region((cs->hw.teles3.cfg_reg), 97)) { printk(KERN_WARNING "HiSax: %s ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 96); + CardType[cs->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 96); return (0); } else - request_region(sp->hscx[0], 97, "HiSax Teles PCMCIA"); + request_region(cs->hw.teles3.hscx[0], 97, "HiSax Teles PCMCIA"); } else { - if (sp->cfg_reg) { - if (check_region((sp->cfg_reg), 8)) { - printk(KERN_WARNING - "HiSax: %s config port %x-%x already in use\n", - CardType[card->typ], - sp->cfg_reg, - sp->cfg_reg + 8); - return (0); + if (cs->hw.teles3.cfg_reg) { + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + if (check_region((cs->hw.teles3.cfg_reg), 1)) { + printk(KERN_WARNING + "HiSax: %s config port %x already in use\n", + CardType[card->typ], + cs->hw.teles3.cfg_reg); + return (0); + } else + request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg"); } else { - request_region(sp->cfg_reg, 8, "teles3 cfg"); + if (check_region((cs->hw.teles3.cfg_reg), 8)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 8); + return (0); + } else + request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg"); } } - if (check_region((sp->isac), 32)) { + if (check_region((cs->hw.teles3.isac + 32), 32)) { printk(KERN_WARNING "HiSax: %s isac ports %x-%x already in use\n", - CardType[sp->typ], - sp->isac, - sp->isac + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } + CardType[cs->typ], + cs->hw.teles3.isac + 32, + cs->hw.teles3.isac + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } return (0); - } else { - request_region(sp->isac, 32, "HiSax isac"); - } - if (check_region((sp->hscx[0]), 32)) { + } else + request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac"); + if (check_region((cs->hw.teles3.hscx[0] + 32), 32)) { printk(KERN_WARNING "HiSax: %s hscx A ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[0], - sp->hscx[0] + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } - release_ioregs(card, 1); + CardType[cs->typ], + cs->hw.teles3.hscx[0] + 32, + cs->hw.teles3.hscx[0] + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 1); return (0); - } else { - request_region(sp->hscx[0], 32, "HiSax hscx A"); - } - if (check_region((sp->hscx[1]), 32)) { + } else + request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A"); + if (check_region((cs->hw.teles3.hscx[1] + 32), 32)) { printk(KERN_WARNING "HiSax: %s hscx B ports %x-%x already in use\n", - CardType[sp->typ], - sp->hscx[1], - sp->hscx[1] + 32); - if (sp->cfg_reg) { - release_region(sp->cfg_reg, 8); - } - release_ioregs(card, 3); + CardType[cs->typ], + cs->hw.teles3.hscx[1] + 32, + cs->hw.teles3.hscx[1] + 64); + if (cs->hw.teles3.cfg_reg) + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + release_ioregs(cs, 3); return (0); - } else { - request_region(sp->hscx[1], 32, "HiSax hscx B"); - } - switch (sp->irq) { - case 2: - cfval = 0x00; - break; - case 3: - cfval = 0x02; - break; - case 4: - cfval = 0x04; - break; - case 5: - cfval = 0x06; - break; - case 10: - cfval = 0x08; - break; - case 11: - cfval = 0x0A; - break; - case 12: - cfval = 0x0C; - break; - case 15: - cfval = 0x0E; - break; - default: - cfval = 0x00; - break; - } + } else + request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B"); } - if (sp->cfg_reg) { - if ((val = bytein(sp->cfg_reg + 0)) != 0x51) { + if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) { + if ((val = bytein(cs->hw.teles3.cfg_reg + 0)) != 0x51) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 0, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 0, val); + release_io_teles3(cs); return (0); } - if ((val = bytein(sp->cfg_reg + 1)) != 0x93) { + if ((val = bytein(cs->hw.teles3.cfg_reg + 1)) != 0x93) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 1, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 1, val); + release_io_teles3(cs); return (0); } - val = bytein(sp->cfg_reg + 2); /* 0x1e=without AB - * 0x1f=with AB - * 0x1c 16.3 ??? - * 0x46 16.3 with AB + Video (Teles-Vision) - */ - if (val != 0x46 && val != 0x1c && val != 0x1e && val != 0x1f) { + val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB + * 0x1f=with AB + * 0x1c 16.3 ??? + * 0x39 16.3 1.1 + * 0x46 16.3 with AB + Video (Teles-Vision) + */ + if (val != 0x46 && val != 0x39 && val != 0x1c && val != 0x1e && val != 0x1f) { printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", - sp->cfg_reg + 2, val); - release_io_teles3(card); + cs->hw.teles3.cfg_reg + 2, val); + release_io_teles3(cs); return (0); } - save_flags(flags); - byteout(sp->cfg_reg + 4, cfval); - sti(); - HZDELAY(HZ / 10 + 1); - byteout(sp->cfg_reg + 4, cfval | 1); - HZDELAY(HZ / 10 + 1); - restore_flags(flags); - } else { - /* Reset off for 16.3 PnP , thanks to Georg Acher */ - save_flags(flags); - byteout(sp->isac + 0x1c, 1); - HZDELAY(2); - restore_flags(flags); } - printk(KERN_NOTICE - "HiSax: %s config irq:%d isac:%x cfg:%x\n", - CardType[sp->typ], sp->irq, - sp->isac, sp->cfg_reg); - printk(KERN_NOTICE - "HiSax: hscx A:%x hscx B:%x\n", - sp->hscx[0], sp->hscx[1]); - verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf; - verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf; - printk(KERN_INFO "Teles3: HSCX version A: %s B: %s\n", - HscxVersion(verA), HscxVersion(verB)); - val = readreg(sp->isac, ISAC_RBCH); - printk(KERN_INFO "Teles3: ISAC %s\n", - ISACVersion(val)); - if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) { + printk(KERN_INFO + "HiSax: %s config irq:%d isac:0x%X cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg); + printk(KERN_INFO + "HiSax: hscx A:0x%X hscx B:0x%X\n", + cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32); + + if (reset_teles3(cs)) { + printk(KERN_WARNING "Teles3: wrong IRQ\n"); + release_io_teles3(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Teles_card_msg; + ISACVersion(cs, "Teles3:"); + if (HscxVersion(cs, "Teles3:")) { printk(KERN_WARNING "Teles3: wrong HSCX versions check IO address\n"); - release_io_teles3(card); + release_io_teles3(cs); return (0); } - sp->modehscx = &modehscx; - sp->ph_command = &ph_command; - sp->hscx_fill_fifo = &hscx_fill_fifo; - sp->isac_fill_fifo = &isac_fill_fifo; return (1); } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/teles3c.c linux/drivers/isdn/hisax/teles3c.c --- v2.0.35/linux/drivers/isdn/hisax/teles3c.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/teles3c.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,196 @@ +/* $Id: teles3c.c,v 1.1.2.3 1998/11/03 00:07:40 keil Exp $ + + * teles3c.c low level stuff for teles 16.3c + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * + * $Log: teles3c.c,v $ + * Revision 1.1.2.3 1998/11/03 00:07:40 keil + * certification related changes + * fixed logging for smaller stack use + * + * Revision 1.1.2.2 1998/01/27 22:40:37 keil + * fixed IRQ latency, B-channel selection and more + * + * Revision 1.1.2.1 1998/01/11 22:54:04 keil + * Teles 16.3c (HFC 2BDS0) first version + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_2bds0.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +const char *teles163c_revision = "$Revision: 1.1.2.3 $"; + +static void +t163c_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat; + + if (!cs) { + printk(KERN_WARNING "teles3c: Spurious interrupt!\n"); + return; + } + if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & + (stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) { + val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "teles3c: stat(%02x) s1(%02x)", stat, val); + hfc2bds0_interrupt(cs, val); + } else { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "teles3c: irq_no_irq stat(%02x)", stat); + } +} + +static void +t163c_Timer(struct IsdnCardState *cs) +{ + cs->hw.hfcD.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80); + add_timer(&cs->hw.hfcD.timer); +*/ +} + +void +release_io_t163c(struct IsdnCardState *cs) +{ + release2bds0(cs); + del_timer(&cs->hw.hfcD.timer); + if (cs->hw.hfcD.addr) + release_region(cs->hw.hfcD.addr, 2); +} + +static void +reset_t163c(struct IsdnCardState *cs) +{ + long flags; + + printk(KERN_INFO "teles3c: resetting card\n"); + cs->hw.hfcD.cirm = HFCD_RESET | HFCD_MEM8K; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 3; + schedule(); + cs->hw.hfcD.cirm = HFCD_MEM8K; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + cs->hw.hfcD.cirm |= HFCD_INTB; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* INT B */ + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */ + cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE; + cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS | + HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC | + HFCD_INTS_DREC | HFCD_INTS_L1STATE; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2); + 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.sctrl = 0; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl); + restore_flags(flags); +} + +static int +t163c_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "teles3c: card_msg %x", mt); + switch (mt) { + case CARD_RESET: + reset_t163c(cs); + return(0); + case CARD_RELEASE: + release_io_t163c(cs); + return(0); + case CARD_SETIRQ: + cs->hw.hfcD.timer.expires = jiffies + 75; + add_timer(&cs->hw.hfcD.timer); + return(request_irq(cs->irq, &t163c_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + init2bds0(cs); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + (80*HZ)/1000; + schedule(); + cs->hw.hfcD.ctmt |= HFCD_TIM800; + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); + cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); + restore_flags(flags); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +__initfunc(int +setup_t163c(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, teles163c_revision); + printk(KERN_INFO "HiSax: Teles 16.3c driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELES3C) + return (0); + cs->debug = 0xff; + cs->hw.hfcD.addr = card->para[1] & 0xfffe; + cs->irq = card->para[0]; + cs->hw.hfcD.cip = 0; + cs->hw.hfcD.int_s1 = 0; + cs->hw.hfcD.send = NULL; + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->hw.hfcD.bfifosize = 1024 + 512; + cs->hw.hfcD.dfifosize = 512; + cs->ph_state = 0; + cs->hw.hfcD.fifo = 255; + if (check_region((cs->hw.hfcD.addr), 2)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.hfcD.addr, + cs->hw.hfcD.addr + 2); + return (0); + } else { + request_region(cs->hw.hfcD.addr, 2, "teles3c isdn"); + } + /* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */ + outb(0x00, cs->hw.hfcD.addr); + outb(0x56, cs->hw.hfcD.addr | 1); + printk(KERN_INFO + "teles3c: defined at 0x%x IRQ %d HZ %d\n", + cs->hw.hfcD.addr, + cs->irq, HZ); + + set_cs_func(cs); + cs->hw.hfcD.timer.function = (void *) t163c_Timer; + cs->hw.hfcD.timer.data = (long) cs; + init_timer(&cs->hw.hfcD.timer); + reset_t163c(cs); + cs->cardmsg = &t163c_card_msg; + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/telespci.c linux/drivers/isdn/hisax/telespci.c --- v2.0.35/linux/drivers/isdn/hisax/telespci.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/hisax/telespci.c Sun Nov 15 10:32:58 1998 @@ -0,0 +1,377 @@ +/* $Id: telespci.c,v 1.1.2.3 1998/10/16 12:46:09 keil Exp $ + + * telespci.c low level stuff for Teles PCI isdn cards + * + * Author Ton van Rosmalen + * Karsten Keil (keil@temic-ech.spacenet.de) + * + * + * $Log: telespci.c,v $ + * Revision 1.1.2.3 1998/10/16 12:46:09 keil + * fix pci detection for more as one card + * + * Revision 1.1.2.2 1998/04/20 08:52:46 keil + * Fix register offsets + * + * Revision 1.1.2.1 1998/04/11 18:44:42 keil + * New files + * + * + */ +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" +#include +#include + +extern const char *CardType[]; + +const char *telespci_revision = "$Revision: 1.1.2.3 $"; + +#define ZORAN_PO_RQ_PEN 0x02000000 +#define ZORAN_PO_WR 0x00800000 +#define ZORAN_PO_GID0 0x00000000 +#define ZORAN_PO_GID1 0x00100000 +#define ZORAN_PO_GREG0 0x00000000 +#define ZORAN_PO_GREG1 0x00010000 +#define ZORAN_PO_DMASK 0xFF + +#define WRITE_ADDR_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0) +#define READ_DATA_ISAC (ZORAN_PO_GID0 | ZORAN_PO_GREG1) +#define WRITE_DATA_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1) +#define WRITE_ADDR_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0) +#define READ_DATA_HSCX (ZORAN_PO_GID1 | ZORAN_PO_GREG1) +#define WRITE_DATA_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1) + +#define ZORAN_WAIT_NOBUSY do { \ + portdata = readl(adr + 0x200); \ + } while (portdata & ZORAN_PO_RQ_PEN) + +static inline u_char +readisac(unsigned int adr, u_char off) +{ + register unsigned int portdata; + + ZORAN_WAIT_NOBUSY; + + /* set address for ISAC */ + writel(WRITE_ADDR_ISAC | off, adr + 0x200); + ZORAN_WAIT_NOBUSY; + + /* read data from ISAC */ + writel(READ_DATA_ISAC, adr + 0x200); + ZORAN_WAIT_NOBUSY; + return((u_char)(portdata & ZORAN_PO_DMASK)); +} + +static inline void +writeisac(unsigned int adr, u_char off, u_char data) +{ + register unsigned int portdata; + + ZORAN_WAIT_NOBUSY; + + /* set address for ISAC */ + writel(WRITE_ADDR_ISAC | off, adr + 0x200); + ZORAN_WAIT_NOBUSY; + + /* write data to ISAC */ + writel(WRITE_DATA_ISAC | data, adr + 0x200); + ZORAN_WAIT_NOBUSY; +} + +static inline u_char +readhscx(unsigned int adr, int hscx, u_char off) +{ + register unsigned int portdata; + + ZORAN_WAIT_NOBUSY; + /* set address for HSCX */ + writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200); + ZORAN_WAIT_NOBUSY; + + /* read data from HSCX */ + writel(READ_DATA_HSCX, adr + 0x200); + ZORAN_WAIT_NOBUSY; + return ((u_char)(portdata & ZORAN_PO_DMASK)); +} + +static inline void +writehscx(unsigned int adr, int hscx, u_char off, u_char data) +{ + register unsigned int portdata; + + ZORAN_WAIT_NOBUSY; + /* set address for HSCX */ + writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200); + ZORAN_WAIT_NOBUSY; + + /* write data to HSCX */ + writel(WRITE_DATA_HSCX | data, adr + 0x200); + ZORAN_WAIT_NOBUSY; +} + +static inline void +read_fifo_isac(unsigned int adr, u_char * data, int size) +{ + register unsigned int portdata; + register int i; + + ZORAN_WAIT_NOBUSY; + /* read data from ISAC */ + for (i = 0; i < size; i++) { + /* set address for ISAC fifo */ + writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200); + ZORAN_WAIT_NOBUSY; + writel(READ_DATA_ISAC, adr + 0x200); + ZORAN_WAIT_NOBUSY; + data[i] = (u_char)(portdata & ZORAN_PO_DMASK); + } +} + +static void +write_fifo_isac(unsigned int adr, u_char * data, int size) +{ + register unsigned int portdata; + register int i; + + ZORAN_WAIT_NOBUSY; + /* write data to ISAC */ + for (i = 0; i < size; i++) { + /* set address for ISAC fifo */ + writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200); + ZORAN_WAIT_NOBUSY; + writel(WRITE_DATA_ISAC | data[i], adr + 0x200); + ZORAN_WAIT_NOBUSY; + } +} + +static inline void +read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +{ + register unsigned int portdata; + register int i; + + ZORAN_WAIT_NOBUSY; + /* read data from HSCX */ + for (i = 0; i < size; i++) { + /* set address for HSCX fifo */ + writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200); + ZORAN_WAIT_NOBUSY; + writel(READ_DATA_HSCX, adr + 0x200); + ZORAN_WAIT_NOBUSY; + data[i] = (u_char) (portdata & ZORAN_PO_DMASK); + } +} + +static inline void +write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size) +{ + unsigned int portdata; + register int i; + + ZORAN_WAIT_NOBUSY; + /* write data to HSCX */ + for (i = 0; i < size; i++) { + /* set address for HSCX fifo */ + writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200); + ZORAN_WAIT_NOBUSY; + writel(WRITE_DATA_HSCX | data[i], adr + 0x200); + ZORAN_WAIT_NOBUSY; + udelay(10); + } +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readisac(cs->hw.teles0.membase, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writeisac(cs->hw.teles0.membase, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + read_fifo_isac(cs->hw.teles0.membase, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + write_fifo_isac(cs->hw.teles0.membase, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readhscx(cs->hw.teles0.membase, hscx, offset)); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writehscx(cs->hw.teles0.membase, hscx, offset, value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg) +#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt) + +#include "hscx_irq.c" + +static void +telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ +#define MAXCOUNT 20 + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "TelesPCI: Spurious interrupt!\n"); + return; + } + val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA); + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readisac(cs->hw.teles0.membase, ISAC_ISTA); + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + /* Clear interrupt register for Zoran PCI controller */ + writel(0x70000000, cs->hw.teles0.membase + 0x3C); + + if (stat & 1) { + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF); + writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0); + writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0); + } + if (stat & 2) { + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF); + writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0); + } +} + +void +release_io_telespci(struct IsdnCardState *cs) +{ + vfree((void *)cs->hw.teles0.membase); +} + +static int +TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + return(0); + case CARD_RELEASE: + release_io_telespci(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &telespci_interrupt, + I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + +static int pci_index __initdata = 0; + +__initfunc(int +setup_telespci(struct IsdnCard *card)) +{ + int found=0; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + u_char pci_bus, pci_device_fn, pci_irq; + u_int pci_memaddr; + + strcpy(tmp, telespci_revision); + printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_TELESPCI) + return (0); + +#if CONFIG_PCI + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device (0x11DE, 0x6120, + pci_index, &pci_bus, &pci_device_fn) + == PCIBIOS_SUCCESSFUL) { + found = 1; + } else { + break; + } + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_memaddr); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + + printk(KERN_INFO "Found: Zoran, base-address: 0x%x," + " irq: 0x%x\n", pci_memaddr, pci_irq); + break; + } + if (!found) { + printk(KERN_WARNING "TelesPCI: No PCI card found\n"); + return(0); + } + pci_index++; + cs->hw.teles0.membase = (u_int) vremap(pci_memaddr, PAGE_SIZE); + cs->irq = pci_irq; +#else + printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n"); + printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n"); + return (0); +#endif /* CONFIG_PCI */ + + /* Initialize Zoran PCI controller */ + writel(0x00000000, cs->hw.teles0.membase + 0x28); + writel(0x01000000, cs->hw.teles0.membase + 0x28); + writel(0x01000000, cs->hw.teles0.membase + 0x28); + writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C); + writel(0x70000000, cs->hw.teles0.membase + 0x3C); + writel(0x61000000, cs->hw.teles0.membase + 0x40); + /* writel(0x00800000, cs->hw.teles0.membase + 0x200); */ + + printk(KERN_INFO + "HiSax: %s config irq:%d mem:%x\n", + CardType[cs->typ], cs->irq, + cs->hw.teles0.membase); + + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &TelesPCI_card_msg; + ISACVersion(cs, "TelesPCI:"); + if (HscxVersion(cs, "TelesPCI:")) { + printk(KERN_WARNING + "TelesPCI: wrong HSCX versions check IO/MEM addresses\n"); + release_io_telespci(cs); + return (0); + } + return (1); +} diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c --- v2.0.35/linux/drivers/isdn/icn/icn.c Tue Aug 5 17:10:02 1997 +++ linux/drivers/isdn/icn/icn.c Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: icn.c,v 1.45 1997/06/21 10:42:06 fritz Exp $ + /* $Id: icn.c,v 1.45.2.4 1998/11/05 22:13:02 fritz Exp $ * ISDN low-level module for the ICN active ISDN-Card. * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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 @@ -19,6 +19,28 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.c,v $ + * Revision 1.45.2.4 1998/11/05 22:13:02 fritz + * Changed mail-address. + * + * Revision 1.45.2.3 1998/06/07 13:32:04 fritz + * Minor bugfixes for broken Switches. + * + * Revision 1.45.2.2 1998/03/07 23:35:36 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * + * Revision 1.45.2.1 1997/08/21 15:56:50 fritz + * Synchronized 2.0.X branch with 2.0.31-pre7 + * * Revision 1.45 1997/06/21 10:42:06 fritz * Added availability to select leased mode on only one channel. * @@ -193,7 +215,7 @@ #undef MAP_DEBUG static char -*revision = "$Revision: 1.45 $"; +*revision = "$Revision: 1.45.2.4 $"; static int icn_addcard(int, char *, char *); @@ -531,8 +553,13 @@ { {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ - {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ - {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ +#ifdef CONFIG_ISDN_WITH_ABC + {"DCON_", ISDN_STAT_DCONN, 10}, /* D-Channel connected */ + {"DDIS_", ISDN_STAT_DHUP, 11}, /* D-Channel disconnected */ +#else + {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ + {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ +#endif {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */ {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */ {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */ @@ -582,7 +609,41 @@ cmd.driver = card->myid; cmd.arg = channel; switch (action) { +#ifdef CONFIG_ISDN_WITH_ABC + case 11: + + save_flags(flags); + cli(); + icn_free_queue(card,channel); + card->rcvidx[channel] = 0; + + if( card->flags & + ((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) { + + isdn_ctrl ncmd; + + printk(KERN_INFO "icn: D-Channel hangup before B-Channel hangup\n"); + + card->flags &= ~((channel)? + ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE); + + memset(&ncmd,0,sizeof(ncmd)); + + ncmd.driver = card->myid; + ncmd.arg = channel; + ncmd.command = ISDN_STAT_BHUP; + restore_flags(flags); + card->interface.statcallb(&cmd); + dflag |= (channel+1); + + } else restore_flags(flags); + + break; +#endif case 1: +#ifdef CONFIG_ISDN_WITH_ABC + icn_free_queue(card,channel); +#endif card->flags |= (channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE; break; @@ -601,17 +662,22 @@ char *t = status + 6; char *s = strpbrk(t, ","); + memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup)); + if (!s) + break; *s++ = '\0'; strncpy(cmd.parm.setup.phone, t, sizeof(cmd.parm.setup.phone)); - s = strpbrk(t = s, ","); + if (!(s = strpbrk(t = s, ","))) + break; *s++ = '\0'; if (!strlen(t)) cmd.parm.setup.si1 = 0; else cmd.parm.setup.si1 = simple_strtoul(t, NULL, 10); - s = strpbrk(t = s, ","); + if (!(s = strpbrk(t = s, ","))) + break; *s++ = '\0'; if (!strlen(t)) cmd.parm.setup.si2 = 0; @@ -621,8 +687,6 @@ strncpy(cmd.parm.setup.eazmsn, s, sizeof(cmd.parm.setup.eazmsn)); } - cmd.parm.setup.plan = 0; - cmd.parm.setup.screen = 0; break; case 4: sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h --- v2.0.35/linux/drivers/isdn/icn/icn.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/icn/icn.h Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: icn.h,v 1.26 1997/02/14 12:23:16 fritz Exp $ +/* $Id: icn.h,v 1.26.2.1 1998/11/05 22:13:06 fritz Exp $ * ISDN lowlevel-module for the ICN active ISDN-Card. * - * Copyright 1994 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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 @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: icn.h,v $ + * Revision 1.26.2.1 1998/11/05 22:13:06 fritz + * Changed mail-address. + * * Revision 1.26 1997/02/14 12:23:16 fritz * Added support for new insmod parameter handling. * diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c --- v2.0.35/linux/drivers/isdn/isdn_audio.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_audio.c Sun Nov 15 10:32:59 1998 @@ -1,9 +1,10 @@ -/* $Id: isdn_audio.c,v 1.8 1997/03/02 14:29:16 fritz Exp $ +/* $Id: isdn_audio.c,v 1.8.2.2 1998/11/05 22:11:35 fritz Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at) + * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.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 @@ -20,6 +21,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.c,v $ + * Revision 1.8.2.2 1998/11/05 22:11:35 fritz + * Changed mail-address. + * + * Revision 1.8.2.1 1998/08/22 16:43:04 armin + * Added silence detection in audio receive mode (AT+VSD). + * * Revision 1.8 1997/03/02 14:29:16 fritz * More ttyI related cleanup. * @@ -53,7 +60,7 @@ #include "isdn_audio.h" #include "isdn_common.h" -char *isdn_audio_revision = "$Revision: 1.8 $"; +char *isdn_audio_revision = "$Revision: 1.8.2.2 $"; /* * Misc. lookup-tables. @@ -652,4 +659,93 @@ } len -= c; } +} + +silence_state * +isdn_audio_silence_init(silence_state * s) +{ + if (!s) + s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC); + if (s) { + s->idx = 0; + s->state = 0; + } + return s; +} + +void +isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt) +{ + silence_state *s = info->silence_state; + int i; + signed char c; + + if ((!s) || (!info->emu.vpar[1])) return; + + for (i = 0; i < len; i++) { + if (fmt) + c = isdn_audio_alaw_to_ulaw[*buf++]; + else + c = *buf++; + + if (c > 0) c -= 128; + c = abs(c); + + if (c > (info->emu.vpar[1] * 4)) { + s->idx = 0; + s->state = 1; + } else { + if (s->idx < 210000) s->idx++; + } + } +} + +void +isdn_audio_eval_silence(modem_info * info) +{ + silence_state *s = info->silence_state; + struct sk_buff *skb; + unsigned long flags; + int di; + int ch; + char what; + char *p; + + if (!s) return; + what = ' '; + if (s->idx > (info->emu.vpar[2] * 800)) { + s->idx = 0; + if (!s->state) { /* silence from beginning of rec */ + what = 's'; + } else { + what = 'q'; + } + } + if ((what == 's') || (what == 'q')) { + printk(KERN_DEBUG "ttyI%d: %s\n", info->line, + (what=='s') ? "silence":"quiet"); + skb = dev_alloc_skb(2); + p = (char *) skb_put(skb, 2); + p[0] = 0x10; + p[1] = what; + if (skb_headroom(skb) < sizeof(isdn_audio_skb)) { + printk(KERN_WARNING + "isdn_audio: insufficient skb_headroom, dropping\n"); + kfree_skb(skb, FREE_READ); + return; + } + ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; + ISDN_AUDIO_SKB_LOCK(skb) = 0; + save_flags(flags); + cli(); + di = info->isdn_driver; + ch = info->isdn_channel; + __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); + dev->drv[di]->rcvcount[ch] += 2; + restore_flags(flags); + /* Schedule dequeuing */ + if ((dev->modempoll) && (info->rcvsched)) + isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); + wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); + } } diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_audio.h linux/drivers/isdn/isdn_audio.h --- v2.0.35/linux/drivers/isdn/isdn_audio.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_audio.h Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_audio.h,v 1.5 1997/02/03 22:45:21 fritz Exp $ +/* $Id: isdn_audio.h,v 1.5.2.2 1998/11/05 22:11:39 fritz Exp $ * Linux ISDN subsystem, audio conversion and compression (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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 @@ -19,6 +19,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_audio.h,v $ + * Revision 1.5.2.2 1998/11/05 22:11:39 fritz + * Changed mail-address. + * + * Revision 1.5.2.1 1998/08/22 16:43:06 armin + * Added silence detection in audio receive mode (AT+VSD). + * * Revision 1.5 1997/02/03 22:45:21 fritz * Reformatted according CodingStyle * @@ -51,6 +57,11 @@ int buf[DTMF_NPOINTS]; } dtmf_state; +typedef struct silence_state { + int state; + unsigned int idx; +} silence_state; + extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long); extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long); extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int); @@ -60,3 +71,6 @@ extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int); extern void isdn_audio_eval_dtmf(modem_info *); dtmf_state *isdn_audio_dtmf_init(dtmf_state *); +extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int); +extern void isdn_audio_eval_silence(modem_info *); +silence_state *isdn_audio_silence_init(silence_state *); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c --- v2.0.35/linux/drivers/isdn/isdn_cards.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_cards.c Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_cards.c,v 1.6 1997/04/23 18:56:03 fritz Exp $ +/* $Id: isdn_cards.c,v 1.6.2.1 1998/11/05 22:11:40 fritz Exp $ * Linux ISDN subsystem, initialization for non-modularized drivers. * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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 @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.c,v $ + * Revision 1.6.2.1 1998/11/05 22:11:40 fritz + * Changed mail-address. + * * Revision 1.6 1997/04/23 18:56:03 fritz * Old Teles driver removed, Changed doc and scripts accordingly. * diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_cards.h linux/drivers/isdn/isdn_cards.h --- v2.0.35/linux/drivers/isdn/isdn_cards.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_cards.h Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_cards.h,v 1.2 1997/02/03 23:31:55 fritz Exp $ +/* $Id: isdn_cards.h,v 1.2.2.1 1998/11/05 22:11:42 fritz Exp $ * Linux ISDN subsystem, initialization for non-modularized drivers. * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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 @@ -19,6 +19,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_cards.h,v $ + * Revision 1.2.2.1 1998/11/05 22:11:42 fritz + * Changed mail-address. + * * Revision 1.2 1997/02/03 23:31:55 fritz * Reformatted according CodingStyle * diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c --- v2.0.35/linux/drivers/isdn/isdn_common.c Tue Aug 5 11:05:16 1997 +++ linux/drivers/isdn/isdn_common.c Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $ +/* $Id: isdn_common.c,v 1.44.2.9 1998/11/06 00:07:25 fritz Exp $ * Linux ISDN subsystem, common used functions (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) * @@ -21,6 +21,43 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.c,v $ + * Revision 1.44.2.9 1998/11/06 00:07:25 fritz + * Bugfix: loading additional driver while /dev/isdnctrl opened resulted in + * wrong module usage count after closing /dev/isdnctrl. + * + * Revision 1.44.2.8 1998/11/05 22:11:43 fritz + * Changed mail-address. + * + * Revision 1.44.2.7 1998/11/04 17:22:40 fritz + * Replaced broken lowlevel-driver locking. + * + * Revision 1.44.2.6 1998/11/03 14:30:56 fritz + * Reduced stack usage in various functions. + * Adapted statemachine to work with certified HiSax. + * Some fixes in callback handling. + * + * Revision 1.44.2.5 1998/10/25 15:48:04 fritz + * Misc bugfixes and adaptions to new HiSax + * + * Revision 1.44.2.4 1998/06/07 13:47:44 fritz + * ABC cleanup + * + * Revision 1.44.2.2 1998/03/16 09:55:44 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * + * Revision 1.44.2.1 1998/03/07 23:35:03 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * * Revision 1.44 1997/05/27 15:17:23 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -218,7 +255,7 @@ isdn_dev *dev = (isdn_dev *) 0; -static char *isdn_revision = "$Revision: 1.44 $"; +static char *isdn_revision = "$Revision: 1.44.2.9 $"; extern char *isdn_net_revision; extern char *isdn_tty_revision; @@ -238,16 +275,39 @@ void isdn_MOD_INC_USE_COUNT(void) { + int i; + MOD_INC_USE_COUNT; + for (i = 0; i < dev->drivers; i++) { + isdn_ctrl cmd; + + cmd.driver = i; + cmd.arg = 0; + cmd.command = ISDN_CMD_LOCK; + isdn_command(&cmd); + dev->drv[i]->locks++; + } } void isdn_MOD_DEC_USE_COUNT(void) { + int i; + MOD_DEC_USE_COUNT; + for (i = 0; i < dev->drivers; i++) + if (dev->drv[i]->locks > 0) { + isdn_ctrl cmd; + + cmd.driver = i; + cmd.arg = 0; + cmd.command = ISDN_CMD_UNLOCK; + isdn_command(&cmd); + dev->drv[i]->locks--; + } } -#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) +#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) || defined(CONFIG_ISDN_TIMEOUT_RULES) void isdn_dumppkt(char *s, u_char * p, int len, int dumplen) { @@ -329,6 +389,7 @@ #endif } } + if (tf) { int flags; @@ -392,6 +453,15 @@ isdn_trash_skb(skb, FREE_READ); } +/* + * Intercept command from Linklevel to Lowlevel. + */ +int +isdn_command(isdn_ctrl *cmd) +{ + return dev->drv[cmd->driver]->interface->command(cmd); +} + void isdn_all_eaz(int di, int ch) { @@ -403,7 +473,7 @@ cmd.arg = ch; cmd.command = ISDN_CMD_SETEAZ; cmd.parm.num[0] = '\0'; - (void) dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); } static int @@ -424,7 +494,7 @@ return -1; if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) return 0; if (isdn_tty_stat_callback(i, c)) return 0; @@ -438,13 +508,13 @@ wake_up_interruptible(&dev->drv[di]->st_waitq); break; case ISDN_STAT_RUN: - dev->drv[di]->running = 1; + dev->drv[di]->flags |= DRV_FLAG_RUNNING; for (i = 0; i < ISDN_MAX_CHANNELS; i++) if (dev->drvmap[i] == di) isdn_all_eaz(di, dev->chanmap[i]); break; case ISDN_STAT_STOP: - dev->drv[di]->running = 0; + dev->drv[di]->flags &= ~DRV_FLAG_RUNNING; break; case ISDN_STAT_ICALL: if (i < 0) @@ -456,27 +526,23 @@ cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); return 0; } /* Try to find a network-interface which will accept incoming call */ - cmd.driver = di; - cmd.arg = c->arg; - cmd.command = ISDN_CMD_LOCK; - dev->drv[di]->interface->command(&cmd); - r = isdn_net_find_icall(di, c->arg, i, c->parm.setup); + r = isdn_net_find_icall(di, c, i); switch (r) { case 0: /* No network-device replies. * Try ttyI's */ - if (isdn_tty_find_icall(di, c->arg, c->parm.setup) >= 0) + if (isdn_tty_find_icall(di, c) >= 0) retval = 1; - else if (dev->drv[di]->reject_bus) { + else if (dev->drv[di]->flags & DRV_FLAG_REJBUS) { cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); retval = 2; } break; @@ -486,7 +552,7 @@ cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_ACCEPTD; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); retval = 1; break; case 2: /* For calling back, first reject incoming call ... */ @@ -496,7 +562,7 @@ cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_HANGUP; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); if (r == 3) break; /* Fall through */ @@ -505,12 +571,6 @@ isdn_net_dial(); break; } - if (retval != 1) { - cmd.driver = di; - cmd.arg = c->arg; - cmd.command = ISDN_CMD_UNLOCK; - dev->drv[di]->interface->command(&cmd); - } return retval; break; case ISDN_STAT_CINF: @@ -522,7 +582,7 @@ if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; if (strcmp(c->parm.num, "0")) - isdn_net_stat_callback(i, c->command); + isdn_net_stat_callback(i, c); break; case ISDN_STAT_CAUSE: #ifdef ISDN_DEBUG_STATCALLB @@ -541,14 +601,14 @@ if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; /* Find any net-device, waiting for D-channel setup */ - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; /* Find any ttyI, waiting for D-channel setup */ if (isdn_tty_stat_callback(i, c)) { cmd.driver = di; cmd.arg = c->arg; cmd.command = ISDN_CMD_ACCEPTB; - dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); break; } break; @@ -560,10 +620,10 @@ #endif if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - dev->drv[di]->flags &= ~(1 << (c->arg)); + dev->drv[di]->online &= ~(1 << (c->arg)); isdn_info_update(); /* Signal hangup to network-devices */ - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; if (isdn_tty_stat_callback(i, c)) break; @@ -577,9 +637,9 @@ /* Signal B-channel-connect to network-devices */ if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - dev->drv[di]->flags |= (1 << (c->arg)); + dev->drv[di]->online |= (1 << (c->arg)); isdn_info_update(); - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; if (isdn_tty_stat_callback(i, c)) break; @@ -592,7 +652,7 @@ #endif if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - dev->drv[di]->flags &= ~(1 << (c->arg)); + dev->drv[di]->online &= ~(1 << (c->arg)); isdn_info_update(); if (isdn_tty_stat_callback(i, c)) break; @@ -605,7 +665,7 @@ #endif if (dev->global_flags & ISDN_GLOBAL_STOPPED) return 0; - if (isdn_net_stat_callback(i, c->command)) + if (isdn_net_stat_callback(i, c)) break; if (isdn_tty_stat_callback(i, c)) break; @@ -817,7 +877,7 @@ p = istatbuf + strlen(istatbuf); for (i = 0; i < ISDN_MAX_DRIVERS; i++) { if (dev->drv[i]) { - sprintf(p, "%ld ", dev->drv[i]->flags); + sprintf(p, "%ld ", dev->drv[i]->online); p = istatbuf + strlen(istatbuf); } else { sprintf(p, "? "); @@ -880,7 +940,7 @@ drvidx = isdn_minor2drv(minor); if (drvidx < 0) return -ENODEV; - if (!dev->drv[drvidx]->running) + if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) return -ENODEV; chidx = isdn_minor2chan(minor); save_flags(flags); @@ -943,7 +1003,7 @@ drvidx = isdn_minor2drv(minor); if (drvidx < 0) return -ENODEV; - if (!dev->drv[drvidx]->running) + if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) return -ENODEV; chidx = isdn_minor2chan(minor); while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count) @@ -957,7 +1017,7 @@ /* * We want to use the isdnctrl device to load the firmware * - if (!dev->drv[drvidx]->running) + if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) return -ENODEV; */ if (dev->drv[drvidx]->interface->writecmd) @@ -1044,136 +1104,13 @@ } #endif -static int -isdn_set_allcfg(char *src) -{ - int ret; - int i; - ulong flags; - isdn_net_ioctl_cfg cfg; +typedef union { + char name[10]; + char bname[22]; + isdn_ioctl_struct iocts; isdn_net_ioctl_phone phone; - - if ((ret = isdn_net_rmall())) - return ret; - if ((ret = copy_from_user((char *) &i, src, sizeof(int)))) - return ret; - save_flags(flags); - cli(); - src += sizeof(int); - while (i) { - int phone_len; - int out_flag; - - if ((ret = copy_from_user((char *) &cfg, src, sizeof(cfg)))) { - restore_flags(flags); - return ret; - } - src += sizeof(cfg); - if (!isdn_net_new(cfg.name, NULL)) { - restore_flags(flags); - return -EIO; - } - if ((ret = isdn_net_setcfg(&cfg))) { - restore_flags(flags); - return ret; - } - phone_len = out_flag = 0; - while (out_flag < 2) { - if ((ret = verify_area(VERIFY_READ, src, 1))) { - restore_flags(flags); - return ret; - } - GET_USER(phone.phone[phone_len], src++); - if ((phone.phone[phone_len] == ' ') || - (phone.phone[phone_len] == '\0')) { - if (phone_len) { - phone.phone[phone_len] = '\0'; - strcpy(phone.name, cfg.name); - phone.outgoing = out_flag; - if ((ret = isdn_net_addphone(&phone))) { - restore_flags(flags); - return ret; - } - } else - out_flag++; - phone_len = 0; - } - if (++phone_len >= sizeof(phone.phone)) - printk(KERN_WARNING - "%s: IIOCSETSET phone number too long, ignored\n", - cfg.name); - } - i--; - } - restore_flags(flags); - return 0; -} - -static int -isdn_get_allcfg(char *dest) -{ isdn_net_ioctl_cfg cfg; - isdn_net_ioctl_phone phone; - isdn_net_dev *p; - ulong flags; - int ret; - - /* Walk through netdev-chain */ - save_flags(flags); - cli(); - p = dev->netdev; - while (p) { - if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) { - restore_flags(flags); - return ret; - } - strcpy(cfg.eaz, p->local.msn); - cfg.exclusive = p->local.exclusive; - if (p->local.pre_device >= 0) { - sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device], - p->local.pre_channel); - } else - cfg.drvid[0] = '\0'; - cfg.onhtime = p->local.onhtime; - cfg.charge = p->local.charge; - cfg.l2_proto = p->local.l2_proto; - cfg.l3_proto = p->local.l3_proto; - cfg.p_encap = p->local.p_encap; - cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0; - cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0; - cfg.chargehup = (p->local.hupflags & ISDN_CHARGEHUP) ? 1 : 0; - cfg.ihup = (p->local.hupflags & ISDN_INHUP) ? 1 : 0; - cfg.chargeint = p->local.chargeint; - if (copy_to_user(dest, p->local.name, 10)) { - restore_flags(flags); - return -EFAULT; - } - dest += 10; - if (copy_to_user(dest, (char *) &cfg, sizeof(cfg))) { - restore_flags(flags); - return -EFAULT; - } - dest += sizeof(cfg); - strcpy(phone.name, p->local.name); - phone.outgoing = 0; - if ((ret = isdn_net_getphones(&phone, dest)) < 0) { - restore_flags(flags); - return ret; - } else - dest += ret; - strcpy(phone.name, p->local.name); - phone.outgoing = 1; - if ((ret = isdn_net_getphones(&phone, dest)) < 0) { - restore_flags(flags); - return ret; - } else - dest += ret; - put_user(0, dest); - p = p->next; - } - restore_flags(flags); - return 0; -} +} iocpar_t; static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) @@ -1186,19 +1123,15 @@ int i; char *p; char *s; - union iocpar { - char name[10]; - char bname[22]; - isdn_ioctl_struct iocts; - isdn_net_ioctl_phone phone; - isdn_net_ioctl_cfg cfg; - } iocpar; - -#define name iocpar.name -#define bname iocpar.bname -#define iocts iocpar.iocts -#define phone iocpar.phone -#define cfg iocpar.cfg + iocpar_t *iocpar; + +#define name iocpar->name +#define bname iocpar->bname +#define iocts iocpar->iocts +#define phone iocpar->phone +#define cfg iocpar->cfg + +#define RETURN(r) ({ ret = r; kfree(iocpar); return ret; }) if (minor == ISDN_MINOR_STATUS) { switch (cmd) { @@ -1232,127 +1165,131 @@ if (drvidx < 0) return -ENODEV; chidx = isdn_minor2chan(minor); - if (!dev->drv[drvidx]->running) + if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) return -ENODEV; return 0; } if (minor <= ISDN_MINOR_CTRLMAX) { + if ((iocpar = (iocpar_t *)kmalloc(sizeof(iocpar_t), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING "isdn: Out of memory in isdn_ioctl\n"); + return -ENOMEM; + } switch (cmd) { #ifdef CONFIG_NETDEVICES case IIOCNETAIF: /* Add a network-interface */ if (arg) { if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) - return ret; + RETURN(ret); s = name; } else s = NULL; if ((s = isdn_net_new(s, NULL))) { if ((ret = copy_to_user((char *) arg, s, strlen(s) + 1))) - return ret; - return 0; + RETURN(ret); + RETURN(0); } else - return -ENODEV; + RETURN(-ENODEV); case IIOCNETASL: /* Add a slave to a network-interface */ if (arg) { if ((ret = copy_from_user(bname, (char *) arg, sizeof(bname) - 1))) - return ret; + RETURN(ret); } else - return -EINVAL; + RETURN(-EINVAL); if ((s = isdn_net_newslave(bname))) { if ((ret = copy_to_user((char *) arg, s, strlen(s) + 1))) - return ret; - return 0; + RETURN(ret); + RETURN(0); } else - return -ENODEV; + RETURN(-ENODEV); case IIOCNETDIF: /* Delete a network-interface */ if (arg) { if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) - return ret; - return isdn_net_rm(name); + RETURN(ret); + RETURN(isdn_net_rm(name)); } else - return -EINVAL; + RETURN(-EINVAL); case IIOCNETSCF: /* Set configurable parameters of a network-interface */ if (arg) { if ((ret = copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))) - return ret; - return isdn_net_setcfg(&cfg); + RETURN(ret); + RETURN(isdn_net_setcfg(&cfg)); } else - return -EINVAL; + RETURN(-EINVAL); case IIOCNETGCF: /* Get configurable parameters of a network-interface */ if (arg) { if ((ret = copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))) - return ret; + RETURN(ret); if (!(ret = isdn_net_getcfg(&cfg))) { if ((ret = copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))) - return ret; + RETURN(ret); } - return ret; + RETURN(ret); } else - return -EINVAL; + RETURN(-EINVAL); case IIOCNETANM: /* Add a phone-number to a network-interface */ if (arg) { if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))) - return ret; - return isdn_net_addphone(&phone); + RETURN(ret); + RETURN(isdn_net_addphone(&phone)); } else - return -EINVAL; + RETURN(-EINVAL); case IIOCNETGNM: /* Get list of phone-numbers of a network-interface */ if (arg) { if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))) - return ret; - return isdn_net_getphones(&phone, (char *) arg); + RETURN(ret); + RETURN(isdn_net_getphones(&phone, (char *) arg)); } else - return -EINVAL; + RETURN(-EINVAL); case IIOCNETDNM: /* Delete a phone-number of a network-interface */ if (arg) { if ((ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))) - return ret; - return isdn_net_delphone(&phone); + RETURN(ret); + RETURN(isdn_net_delphone(&phone)); } else - return -EINVAL; + RETURN(-EINVAL); case IIOCNETDIL: /* Force dialing of a network-interface */ if (arg) { if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) - return ret; - return isdn_net_force_dial(name); + RETURN(ret); + RETURN(isdn_net_force_dial(name)); } else - return -EINVAL; + RETURN(-EINVAL); #ifdef CONFIG_ISDN_PPP case IIOCNETALN: if (!arg) - return -EINVAL; + RETURN(-EINVAL); if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) - return ret; - return isdn_ppp_dial_slave(name); + RETURN(ret); + RETURN(isdn_ppp_dial_slave(name)); case IIOCNETDLN: if (!arg) - return -EINVAL; + RETURN(-EINVAL); if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) - return ret; - return isdn_ppp_hangup_slave(name); + RETURN(ret); + RETURN(isdn_ppp_hangup_slave(name)); #endif case IIOCNETHUP: /* Force hangup of a network-interface */ if (!arg) - return -EINVAL; + RETURN(-EINVAL); if ((ret = copy_from_user(name, (char *) arg, sizeof(name)))) - return ret; - return isdn_net_force_hangup(name); + RETURN(ret); + RETURN(isdn_net_force_hangup(name)); break; #endif /* CONFIG_NETDEVICES */ case IIOCSETVER: dev->net_verbose = arg; printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose); - return 0; + RETURN(0); case IIOCSETGST: if (arg) dev->global_flags |= ISDN_GLOBAL_STOPPED; @@ -1360,7 +1297,7 @@ dev->global_flags &= ~ISDN_GLOBAL_STOPPED; printk(KERN_INFO "isdn: Global Mode %s\n", (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running"); - return 0; + RETURN(0); case IIOCSETBRJ: drvidx = -1; if (arg) { @@ -1368,7 +1305,7 @@ char *p; if ((ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)))) - return ret; + RETURN(ret); if (strlen(iocts.drvid)) { if ((p = strchr(iocts.drvid, ','))) *p = 0; @@ -1381,28 +1318,15 @@ } } if (drvidx == -1) - return -ENODEV; - dev->drv[drvidx]->reject_bus = iocts.arg; - return 0; - case IIOCGETSET: - /* Get complete setup (all network-interfaces and profile- - settings of all tty-devices */ - if (arg) - return (isdn_get_allcfg((char *) arg)); - else - return -EINVAL; - break; - case IIOCSETSET: - /* Set complete setup (all network-interfaces and profile- - settings of all tty-devices */ - if (arg) - return (isdn_set_allcfg((char *) arg)); + RETURN(-ENODEV); + if (iocts.arg) + dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS; else - return -EINVAL; - break; + dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS; + RETURN(0); case IIOCSIGPRF: dev->profd = current; - return 0; + RETURN(0); break; case IIOCGETPRF: /* Get all Modem-Profiles */ @@ -1413,20 +1337,20 @@ if ((ret = verify_area(VERIFY_WRITE, (void *) arg, (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS))) - return ret; + RETURN(ret); for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if (copy_to_user(p, dev->mdm.info[i].emu.profile, ISDN_MODEM_ANZREG)) - return -EFAULT; + RETURN(-EFAULT); p += ISDN_MODEM_ANZREG; if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN)) - return -EFAULT; + RETURN(-EFAULT); p += ISDN_MSNLEN; } - return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS; + RETURN((ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS); } else - return -EINVAL; + RETURN(-EINVAL); break; case IIOCSETPRF: /* Set all Modem-Profiles */ @@ -1437,20 +1361,20 @@ if ((ret = verify_area(VERIFY_READ, (void *) arg, (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS))) - return ret; + RETURN(ret); for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if ((ret = copy_from_user(dev->mdm.info[i].emu.profile, p, ISDN_MODEM_ANZREG))) - return ret; + RETURN(ret); p += ISDN_MODEM_ANZREG; if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))) - return ret; + RETURN(ret); p += ISDN_MSNLEN; } - return 0; + RETURN(0); } else - return -EINVAL; + RETURN(-EINVAL); break; case IIOCSETMAP: case IIOCGETMAP: @@ -1460,7 +1384,7 @@ if ((ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)))) - return ret; + RETURN(ret); if (strlen(iocts.drvid)) { drvidx = -1; for (i = 0; i < ISDN_MAX_DRIVERS; i++) @@ -1471,7 +1395,7 @@ } else drvidx = 0; if (drvidx == -1) - return -ENODEV; + RETURN(-ENODEV); if (cmd == IIOCSETMAP) { int loop = 1; @@ -1482,7 +1406,7 @@ while (1) { if ((ret = verify_area(VERIFY_READ, p, 1))) - return ret; + RETURN(ret); GET_USER(bname[j], p++); switch (bname[j]) { case '\0': @@ -1510,31 +1434,31 @@ dev->drv[drvidx]->msn2eaz[i] : "-", (i < 9) ? "," : "\0"); if ((ret = copy_to_user(p, bname, strlen(bname) + 1))) - return ret; + RETURN(ret); p += strlen(bname); } } - return 0; + RETURN(0); } else - return -EINVAL; + RETURN(-EINVAL); case IIOCDBGVAR: if (arg) { if ((ret = copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)))) - return ret; - return 0; + RETURN(ret); + RETURN(0); } else - return -EINVAL; + RETURN(-EINVAL); break; default: if ((cmd & IIOCDRVCTL) == IIOCDRVCTL) cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK; else - return -EINVAL; + RETURN(-EINVAL); if (arg) { int i; char *p; if ((ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)))) - return ret; + RETURN(ret); if (strlen(iocts.drvid)) { if ((p = strchr(iocts.drvid, ','))) *p = 0; @@ -1547,21 +1471,21 @@ } else drvidx = 0; if (drvidx == -1) - return -ENODEV; + RETURN(-ENODEV); if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(isdn_ioctl_struct)))) - return ret; + RETURN(ret); c.driver = drvidx; c.command = ISDN_CMD_IOCTL; c.arg = cmd; memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong)); - ret = dev->drv[drvidx]->interface->command(&c); + ret = isdn_command(&c); memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong)); if ((copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)))) - return -EFAULT; - return ret; + RETURN(-EFAULT); + RETURN(ret); } else - return -EINVAL; + RETURN(-EINVAL); } } #ifdef CONFIG_ISDN_PPP @@ -1575,6 +1499,8 @@ #undef iocts #undef phone #undef cfg + +#undef RETURN } /* @@ -1588,7 +1514,6 @@ uint minor = MINOR(ino->i_rdev); int drvidx; int chidx; - isdn_ctrl c; if (minor == ISDN_MINOR_STATUS) { infostruct *p; @@ -1611,31 +1536,25 @@ if (drvidx < 0) return -ENODEV; chidx = isdn_minor2chan(minor); - if (!dev->drv[drvidx]->running) + if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) return -ENODEV; - if (!(dev->drv[drvidx]->flags & (1 << chidx))) + if (!(dev->drv[drvidx]->online & (1 << chidx))) return -ENODEV; - c.command = ISDN_CMD_LOCK; - c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); - MOD_INC_USE_COUNT; + isdn_MOD_INC_USE_COUNT(); return 0; } if (minor <= ISDN_MINOR_CTRLMAX) { drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); if (drvidx < 0) return -ENODEV; - c.command = ISDN_CMD_LOCK; - c.driver = drvidx; - MOD_INC_USE_COUNT; - (void) dev->drv[drvidx]->interface->command(&c); + isdn_MOD_INC_USE_COUNT(); return 0; } #ifdef CONFIG_ISDN_PPP if (minor <= ISDN_MINOR_PPPMAX) { int ret; if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep))) - MOD_INC_USE_COUNT; + isdn_MOD_INC_USE_COUNT(); return ret; } #endif @@ -1646,13 +1565,13 @@ isdn_close(struct inode *ino, struct file *filep) { uint minor = MINOR(ino->i_rdev); - int drvidx; - isdn_ctrl c; - MOD_DEC_USE_COUNT; if (minor == ISDN_MINOR_STATUS) { infostruct *p = dev->infochain; infostruct *q = NULL; + + + MOD_DEC_USE_COUNT; while (p) { if (p->private == (char *) &(filep->private_data)) { if (q) @@ -1668,24 +1587,12 @@ printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n"); return CLOSEVAL; } - if (minor < ISDN_MINOR_CTRL) { - drvidx = isdn_minor2drv(minor); - if (drvidx < 0) - return CLOSEVAL; - c.command = ISDN_CMD_UNLOCK; - c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); + isdn_MOD_DEC_USE_COUNT(); + if (minor < ISDN_MINOR_CTRL) return CLOSEVAL; - } if (minor <= ISDN_MINOR_CTRLMAX) { - drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); - if (drvidx < 0) - return CLOSEVAL; if (dev->profd == current) dev->profd = NULL; - c.command = ISDN_CMD_UNLOCK; - c.driver = drvidx; - (void) dev->drv[drvidx]->interface->command(&c); return CLOSEVAL; } #ifdef CONFIG_ISDN_PPP @@ -1739,7 +1646,6 @@ int i; ulong flags; ulong features; - isdn_ctrl cmd; save_flags(flags); cli(); @@ -1751,16 +1657,12 @@ if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) && ((pre_dev != d) || (pre_chan != dev->chanmap[i]))) continue; - if ((dev->drv[d]->running)) { + if (dev->drv[d]->flags & DRV_FLAG_RUNNING) { if ((dev->drv[d]->interface->features & features) == features) { if ((pre_dev < 0) || (pre_chan < 0)) { dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; dev->usage[i] |= usage; isdn_info_update(); - cmd.driver = d; - cmd.arg = 0; - cmd.command = ISDN_CMD_LOCK; - (void) dev->drv[d]->interface->command(&cmd); restore_flags(flags); return i; } else { @@ -1768,10 +1670,6 @@ dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; dev->usage[i] |= usage; isdn_info_update(); - cmd.driver = d; - cmd.arg = 0; - cmd.command = ISDN_CMD_LOCK; - (void) dev->drv[d]->interface->command(&cmd); restore_flags(flags); return i; } @@ -1807,11 +1705,12 @@ isdn_free_queue(&dev->drv[di]->rpqueue[ch]); cmd.driver = di; cmd.arg = ch; - cmd.command = ISDN_CMD_UNLOCK; + cmd.command = ISDN_CMD_HANGUP; restore_flags(flags); - (void) dev->drv[di]->interface->command(&cmd); + isdn_command(&cmd); return; } + printk(KERN_DEBUG "Unmatched free_channel d=%d c=%d u=%d\n", di, ch, usage); restore_flags(flags); } @@ -2007,12 +1906,11 @@ } memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * n); d->channels = n; - d->loaded = 1; d->maxbufsize = i->maxbufsize; d->pktcount = 0; d->stavail = 0; - d->running = 0; - d->flags = 0; + d->flags = DRV_FLAG_LOADED; + d->online = 0; d->interface = i; for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) if (!dev->drv[drvidx]) diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h --- v2.0.35/linux/drivers/isdn/isdn_common.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_common.h Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_common.h,v 1.6 1997/02/28 02:32:44 fritz Exp $ +/* $Id: isdn_common.h,v 1.6.2.3 1998/11/05 22:11:50 fritz Exp $ * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) * @@ -21,6 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_common.h,v $ + * Revision 1.6.2.3 1998/11/05 22:11:50 fritz + * Changed mail-address. + * + * Revision 1.6.2.2 1998/10/25 15:48:10 fritz + * Misc bugfixes and adaptions to new HiSax + * + * Revision 1.6.2.1 1998/03/16 09:55:48 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * * Revision 1.6 1997/02/28 02:32:44 fritz * Cleanup: Moved some tty related stuff from isdn_common.c * to isdn_tty.c @@ -60,6 +69,7 @@ extern void isdn_MOD_INC_USE_COUNT(void); extern void isdn_MOD_DEC_USE_COUNT(void); extern void isdn_free_channel(int di, int ch, int usage); +extern int isdn_command(isdn_ctrl *); extern void isdn_all_eaz(int di, int ch); extern int isdn_dc2minor(int di, int ch); extern void isdn_info_update(void); @@ -76,6 +86,6 @@ #else #define isdn_export_syms() #endif -#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) +#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) || defined(CONFIG_ISDN_TIMEOUT_RULES) extern void isdn_dumppkt(char *, u_char *, int, int); #endif diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c --- v2.0.35/linux/drivers/isdn/isdn_net.c Sun Nov 15 10:49:35 1998 +++ linux/drivers/isdn/isdn_net.c Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_net.c,v 1.47 1997/06/21 10:52:05 fritz Exp $ +/* $Id: isdn_net.c,v 1.48.2.27 1998/11/05 22:11:53 fritz Exp $ * Linux ISDN subsystem, network interfaces and related functions (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) * @@ -21,6 +21,135 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.c,v $ + * Revision 1.48.2.27 1998/11/05 22:11:53 fritz + * Changed mail-address. + * + * Revision 1.48.2.26 1998/11/03 14:54:39 fritz + * Applied callback-patch fur bundled RAW-IP by gvz@popocate.hamburg.pop.de + * + * Revision 1.48.2.25 1998/11/03 14:31:05 fritz + * Reduced stack usage in various functions. + * Adapted statemachine to work with certified HiSax. + * Some fixes in callback handling. + * + * Revision 1.48.2.24 1998/10/25 22:08:22 fritz + * Bugfix: Only first number was dialed. + * + * Revision 1.48.2.23 1998/10/25 17:42:18 fritz + * Bugfix: added missing reset of connect-flag. + * + * Revision 1.48.2.22 1998/10/25 15:48:13 fritz + * Misc bugfixes and adaptions to new HiSax + * + * Revision 1.48.2.21 1998/10/23 10:14:02 paul + * Implementation of "dialmode" (successor of "status") + * You also need current isdnctrl for this! + * + * Revision 1.48.2.20 1998/08/03 15:52:00 paul + * various changes from 2.0.3[45] kernel sources, as suggested by + * Oliver.Lauer@coburg.baynet.de + * + * Revision 1.48.2.19 1998/07/30 11:29:32 paul + * printk message only appeared when status is off and interface is rawIP, + * which is confusing for people who don't know about "isdnctrl status on". + * + * Revision 1.48.2.18 1998/06/29 17:08:20 cal + * applied small TimRu-patch by Oliver Lauer + * + * Revision 1.48.2.17 1998/06/26 22:00:47 keil + * tx_queue_len = 5 was too small + * + * Revision 1.48.2.16 1998/06/09 12:24:40 cal + * Changed default of local netdev flags: ISDN_NET_STOPPED is default now, + * so autodial is suppressed for that device until it is switched on using + * 'isdnctrl status dev-name on'. + * + * Revision 1.48.2.15 1998/06/07 13:47:51 fritz + * ABC cleanup + * + * Revision 1.48.2.13 1998/05/22 10:13:07 detabc + * in case of a icmp-unreach condition the tcp-keepalive-entrys + * will be dropped from the internal double-link-list (only abc-extension). + * send icmp unreach only if the skb->protocol == ETH_P_IP + * + * Revision 1.48.2.12 1998/05/21 09:23:56 detabc + * speedup abc-no-dchannel-redial + * + * Revision 1.48.2.11 1998/05/07 19:54:53 detabc + * bugfix in abc_delayed_hangup + * optimize keepalive-tests for abc_rawip + * + * Revision 1.48.2.10 1998/05/06 08:34:04 detabc + * change ICMP_HOST_UNREACH to ICMP_NET_UNREACH (only abc-ext.) + * set dev->tbusy to zero in isdn_net_unreachable() (only abc-ext.) + * drop all new packets and send ICMP_NET_UNREACH for + * min. dialwait to max. dialwait * 6 time. (only abc-ext.) + * change random-deliver of packets (small first) from all emcapsulation + * to only rawip with ABC-Router-Flag enabled. + * + * Revision 1.48.2.9 1998/05/03 17:48:22 detabc + * remove unused dev->tbusy = 1 line (only abc-extension) + * + * Revision 1.48.2.8 1998/04/28 15:11:55 detabc + * fixed the wrong #ifndef CONFIG_ISDN_WITH_ABC define + * + * Revision 1.48.2.7 1998/04/26 11:24:08 detabc + * add abc_delayed_hangup (only with a spezial udp-packet) + * move abc-compress and -crypt from start of transmit to the + * isdn_net_send_skb() function (better for TIMRU and the work is much easyer). + * + * added the abc_tx_queue's in the isdn_net_send_skb(). + * give small-packets a high priority. + * transmit small packest first. + * NOTE: NOTE: NOTE: + * At now with the ABC-EXTENSION will be deliver the pakets in RANDOM-ORDER. + * Please let me know if this a problem. + * + * Revision 1.48.2.6 1998/04/18 17:55:09 detabc + * dropp packets if call's are disabled (only abc-extension) + * add secure callback (only abc-extension) + * this means: if you are the callback-out-side and the remote + * dont reject the call ????? + * in this case the connection is ok !!! but you pay the connection !!!! + * now this will be a configerror and the connection will be dropped . + * also a new call will be disabled for 4 hours. + * incouming-calls are still possible. + * + * Revision 1.48.2.5 1998/04/16 19:24:51 keil + * Fix from vger (tx max qlength) + * + * Revision 1.48.2.4 1998/03/20 12:17:27 detabc + * merge abc-extension with timru-time-rules + * christian please check my changes in the CONFIG_ISDN_TIMEOUT_RULES sources + * please ! think about: + * behind the function isdn_abc_net_start_xmit(), is the first one behind + * the kernel-driver, the paket will be compressed an/or crypted. In this + * case no information availible in the skb->data area. + * + * Fritz !! Please read my remarks in the funktion isdn_net_unreachable() ! + * + * Revision 1.48.2.3 1998/03/16 09:55:51 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * + * Revision 1.48.2.2 1998/03/07 23:35:09 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * + * Revision 1.48.2.1 1997/08/21 15:56:07 fritz + * Synchronized 2.0.X branch with 2.0.31-pre7 + * + * Revision 1.48 1997/06/22 11:57:15 fritz + * Added ability to adjust slave triggerlevel. + * * Revision 1.47 1997/06/21 10:52:05 fritz * Removed wrong SET_SKB_FREE in isdn_net_send_skb() * @@ -30,6 +159,7 @@ * Revision 1.45 1997/06/10 16:24:22 hipp * hard_header changes for syncPPP (now behaves like RAWIP) * + * Revision 1.44 1997/05/27 15:17:26 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -208,7 +338,9 @@ #include #include "isdn_common.h" #include "isdn_net.h" +#ifdef CONFIG_ISDN_PPP #include "isdn_ppp.h" +#endif /* Prototypes */ @@ -218,7 +350,7 @@ static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *); static void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */ -char *isdn_net_revision = "$Revision: 1.47 $"; +char *isdn_net_revision = "$Revision: 1.48.2.27 $"; /* * Code for raw-networking over ISDN @@ -227,14 +359,46 @@ static void isdn_net_unreachable(struct device *dev, struct sk_buff *skb, char *reason) { - printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP\n", - dev->name, reason); - if(skb->protocol==htons(ETH_P_IP)) - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0 + int i; + + if(skb != NULL) { + + u_short proto = ntohs(skb->protocol); + + printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP %s\n", + dev->name, + (reason != NULL) ? reason : "reason unknown", + (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "" ); + + if(proto == ETH_P_IP) { + + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN, 0 +#if (LINUX_VERSION_CODE < 0x02010f) /* 2.1.15 */ + ,dev +#endif + ); + } + } + else { /* dial not triggered by rawIP packet */ + printk(KERN_DEBUG "isdn_net: %s: %s\n", + dev->name, + (reason != NULL) ? reason : "reason unknown"); + } + + for(i = 0; i < DEV_NUMBUFFS; i++) { + struct sk_buff *skb; + + while((skb = skb_dequeue(&dev->buffs[i]))) { + if(ntohs(skb->protocol) == ETH_P_IP) { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0 #if (LINUX_VERSION_CODE < 0x02010f) /* 2.1.15 */ - ,dev + , dev #endif - ); + ); + } + dev_kfree_skb(skb, FREE_WRITE); + } + } } static void @@ -293,6 +457,21 @@ restore_flags(flags); } +static inline void +isdn_net_unbind_ptr_idx(int idx) +{ + if (idx != -1) { + dev->rx_netdev[idx] = NULL; + dev->st_netdev[idx] = NULL; + } +} + +static inline void +isdn_net_unbind_ptr(int drv, int ch) +{ + isdn_net_unbind_ptr_idx(isdn_dc2minor(drv, ch)); +} + /* * unbind a net-interface (resets interface after an error) */ @@ -314,9 +493,10 @@ if (!lp->master) /* purge only for master device */ dev_purge_queues(&lp->netdev->dev); lp->dialstate = 0; - dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; - dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; - isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET); + if (lp->isdn_device != -1 && lp->isdn_device != -1) { + isdn_net_unbind_ptr(lp->isdn_device, lp->isdn_channel); + isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET); + } lp->flags &= ~ISDN_NET_CONNECTED; lp->isdn_device = -1; lp->isdn_channel = -1; @@ -358,7 +538,13 @@ if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) { anymore = 1; l->huptimer++; - if ((l->onhtime) && (l->huptimer > l->onhtime)) + /* + * only do timeout-hangup + * if interface is configured as AUTO + */ + if ((ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_AUTO) && + (l->onhtime) && + (l->huptimer > l->onhtime)) if (l->hupflags & ISDN_MANCHARGE && l->hupflags & ISDN_CHARGEHUP) { while (jiffies - l->chargetime > l->chargeint) @@ -382,6 +568,12 @@ isdn_net_hangup(&p->dev); } else if (l->hupflags & ISDN_INHUP) isdn_net_hangup(&p->dev); + + + if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(p->local) == ISDN_NET_DM_OFF)) { + isdn_net_hangup(&p->dev); + break; + } } p = (isdn_net_dev *) p->next; } @@ -396,12 +588,14 @@ * Return: 1 = Event handled, 0 = not for us or unknown Event. */ int -isdn_net_stat_callback(int idx, int cmd) +isdn_net_stat_callback(int idx, isdn_ctrl *c) { isdn_net_dev *p = dev->st_netdev[idx]; if (p) { isdn_net_local *lp = &(p->local); + int cmd = c->command; + switch (cmd) { case ISDN_STAT_BSENT: /* A packet has successfully been sent out */ @@ -434,35 +628,55 @@ lp->dialstate++; return 1; case 12: + case 13: lp->dialstate = 5; return 1; } break; case ISDN_STAT_DHUP: /* Either D-Channel-hangup or error during dialout */ - if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) { - lp->flags &= ~ISDN_NET_CONNECTED; - if (lp->first_skb) { - dev_kfree_skb(lp->first_skb, FREE_WRITE); - lp->first_skb = NULL; - } - if (lp->sav_skb) { - dev_kfree_skb(lp->sav_skb, FREE_WRITE); - lp->sav_skb = NULL; - } - isdn_free_channel(lp->isdn_device, lp->isdn_channel, - ISDN_USAGE_NET); + if (lp->flags & ISDN_NET_CONNECTED) { + printk(KERN_INFO "%s: remote %s (%d)\n", lp->name, + lp->dialstate?"abort":"hangup", lp->dialstate); + printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, + lp->charge); +printk(KERN_DEBUG "idx=%d drv=%d ch=%d\n",idx, lp->isdn_device, lp->isdn_channel); + if (!lp->dialstate) { + isdn_all_eaz(lp->isdn_device, lp->isdn_channel); #ifdef CONFIG_ISDN_PPP - isdn_ppp_free(lp); + isdn_ppp_free(lp); #endif - isdn_all_eaz(lp->isdn_device, lp->isdn_channel); - printk(KERN_INFO "%s: remote hangup\n", lp->name); - printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, - lp->charge); - lp->isdn_device = -1; - lp->isdn_channel = -1; - dev->st_netdev[idx] = NULL; - dev->rx_netdev[idx] = NULL; + isdn_net_unbind_channel(lp); + } else { + switch (lp->dialstate) { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + lp->dialstate = 3; + break; + case 11: + break; + case 12: + { + isdn_ctrl cmd; + printk(KERN_INFO "%s: got reject, waiting for callback ...\n", p->local.name); + + p->local.dtimer = 0; + p->local.dialstate = 13; + cmd.driver = p->local.isdn_device; + cmd.command = ISDN_CMD_HANGUP; + cmd.arg = p->local.isdn_channel; + isdn_command(&cmd); + isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel); + } + /* Fall through */ + case 13: + break; + } + } return 1; } break; @@ -568,6 +782,7 @@ if (p->local.dialstate) printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name, p->local.dialstate); #endif + switch (p->local.dialstate) { case 0: /* Nothing to do for this interface */ @@ -587,6 +802,13 @@ break; } anymore = 1; + + if(p->local.dialtimeout > 0) + if(p->local.dialstarted == 0 || jiffies > (p->local.dialstarted + p->local.dialtimeout + p->local.dialwait)) { + p->local.dialstarted = jiffies; + p->local.dialwait_timer = 0; + } + p->local.dialstate++; /* Fall through */ case 2: @@ -594,27 +816,37 @@ cmd.driver = p->local.isdn_device; cmd.arg = p->local.isdn_channel; cmd.command = ISDN_CMD_CLREAZ; - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver)); cmd.command = ISDN_CMD_SETEAZ; - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); p->local.dialretry = 0; anymore = 1; p->local.dialstate++; - /* Falls through */ + /* Fall through */ case 3: /* Setup interface, dial current phone-number, switch to next number. * If list of phone-numbers is exhausted, increment * retry-counter. */ + if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(p->local) == ISDN_NET_DM_OFF)) { + char *s; + if (dev->global_flags & ISDN_GLOBAL_STOPPED) + s = "dial suppressed: isdn system stopped"; + else + s = "dial suppressed: dialmode `off'"; + isdn_net_unreachable(&p->dev, p->local.first_skb, s); + isdn_net_hangup(&p->dev); + break; + } cmd.driver = p->local.isdn_device; cmd.command = ISDN_CMD_SETL2; cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8); - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = p->local.isdn_device; cmd.command = ISDN_CMD_SETL3; cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8); - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = p->local.isdn_device; cmd.arg = p->local.isdn_channel; save_flags(flags); @@ -631,6 +863,16 @@ p->local.dialstate = 4; printk(KERN_INFO "%s: Open leased line ...\n", p->local.name); } else { + if(p->local.dialtimeout > 0) + if(jiffies > (p->local.dialstarted + p->local.dialtimeout)) { + restore_flags(flags); + p->local.dialwait_timer = jiffies + p->local.dialwait; + p->local.dialstarted = 0; + isdn_net_unreachable(&p->dev, p->local.first_skb, "dial: timed out"); + isdn_net_hangup(&p->dev); + break; + } + sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num); /* * Switch to next number or back to start if at end of list. @@ -638,6 +880,17 @@ if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) { p->local.dial = p->local.phone[1]; p->local.dialretry++; + + if (p->local.dialretry > p->local.dialmax) { + restore_flags(flags); + if (p->local.dialtimeout == 0) { + p->local.dialwait_timer = jiffies + p->local.dialwait; + p->local.dialstarted = 0; + isdn_net_unreachable(&p->dev, p->local.first_skb, "dial: tried all numbers dialmax times"); + } + isdn_net_hangup(&p->dev); + break; + } } restore_flags(flags); cmd.command = ISDN_CMD_DIAL; @@ -651,13 +904,14 @@ isdn_info_update(); } printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name, - p->local.dialretry - 1, cmd.parm.setup.phone); + p->local.dialretry, cmd.parm.setup.phone); p->local.dtimer = 0; #ifdef ISDN_DEBUG_NET_DIAL printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device, p->local.isdn_channel); #endif - dev->drv[p->local.isdn_device]->interface->command(&cmd); + cmd.driver = p->local.isdn_device; + isdn_command(&cmd); } p->local.huptimer = 0; p->local.outgoing = 1; @@ -669,20 +923,19 @@ p->local.hupflags &= ~ISDN_HAVECHARGE; } anymore = 1; - p->local.dialstate = - (p->local.cbdelay && - (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4; + if (p->local.flags & ISDN_NET_CBOUT) { + p->local.dialstate = (p->local.cbdelay) ? 12 : 13; + } else + p->local.dialstate = 4; break; case 4: + case 13: /* Wait for D-Channel-connect. - * If timeout and max retries not - * reached, switch back to state 3. + * If timeout, switch back to state 3. + * Dialmax-handling moved to state 3. */ if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) - if (p->local.dialretry < p->local.dialmax) { - p->local.dialstate = 3; - } else - isdn_net_hangup(&p->dev); + p->local.dialstate = 3; anymore = 1; break; case 5: @@ -693,11 +946,12 @@ anymore = 1; p->local.dtimer = 0; p->local.dialstate++; - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); break; case 6: /* Wait for B- or D-Channel-connect. If timeout, * switch back to state 3. + * Dialmax-handling moved to state 3. */ #ifdef ISDN_DEBUG_NET_DIAL printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer); @@ -716,11 +970,12 @@ cmd.driver = p->local.isdn_device; cmd.command = ISDN_CMD_SETL2; cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8); - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = p->local.isdn_device; cmd.command = ISDN_CMD_SETL3; cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8); - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); + if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15) isdn_net_hangup(&p->dev); else { @@ -733,7 +988,7 @@ cmd.driver = p->local.isdn_device; cmd.arg = p->local.isdn_channel; cmd.command = ISDN_CMD_ACCEPTB; - dev->drv[p->local.isdn_device]->interface->command(&cmd); + isdn_command(&cmd); anymore = 1; p->local.dtimer = 0; p->local.dialstate++; @@ -744,7 +999,7 @@ #ifdef ISDN_DEBUG_NET_DIAL printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer); #endif - if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10) + if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT60) isdn_net_hangup(&p->dev); else anymore = 1; @@ -757,16 +1012,17 @@ break; case 12: /* Remote does callback. Hangup after cbdelay, then wait for incoming - * call (in state 4). + * call (in state 13). */ - if (p->local.dtimer++ > p->local.cbdelay) { + if (p->local.dtimer++ > p->local.cbdelay) + { printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->local.name); p->local.dtimer = 0; - p->local.dialstate = 4; + p->local.dialstate = 13; cmd.driver = p->local.isdn_device; cmd.command = ISDN_CMD_HANGUP; cmd.arg = p->local.isdn_channel; - (void) dev->drv[cmd.driver]->interface->command(&cmd); + isdn_command(&cmd); isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel); } anymore = 1; @@ -795,12 +1051,14 @@ #ifdef CONFIG_ISDN_PPP isdn_ppp_free(lp); #endif - cmd.driver = lp->isdn_device; - cmd.command = ISDN_CMD_HANGUP; - cmd.arg = lp->isdn_channel; - (void) dev->drv[cmd.driver]->interface->command(&cmd); + if ((lp->isdn_device != -1) && (lp->isdn_channel != -1)) { + cmd.driver = lp->isdn_device; + cmd.command = ISDN_CMD_HANGUP; + cmd.arg = lp->isdn_channel; + isdn_command(&cmd); + isdn_all_eaz(lp->isdn_device, lp->isdn_channel); + } printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge); - isdn_all_eaz(lp->isdn_device, lp->isdn_channel); } isdn_net_unbind_channel(lp); } @@ -821,9 +1079,6 @@ addinfo[0] = '\0'; switch (lp->p_encap) { - case ISDN_NET_ENCAP_SYNCPPP: - p = &buf[IPPP_MAX_HEADER]; - break; case ISDN_NET_ENCAP_IPTYP: proto = ntohs(*(unsigned short *) &buf[0]); p = &buf[2]; @@ -836,6 +1091,12 @@ proto = ntohs(*(unsigned short *) &buf[2]); p = &buf[4]; break; +#ifdef CONFIG_ISDN_PPP + case ISDN_NET_ENCAP_SYNCPPP: + /* jump over fake header. */ + p = &buf[IPPP_MAX_HEADER]; + break; +#endif } data_ofs = ((p[0] & 15) * 4); switch (proto) { @@ -913,7 +1174,6 @@ return 1; } - /* * Helper function for isdn_net_start_xmit. * When called, the connection is already established. @@ -994,8 +1254,10 @@ if (ndev->tbusy) { if (jiffies - ndev->trans_start < (2 * HZ)) return 1; + if (!lp->dialstate) lp->stats.tx_errors++; + ndev->tbusy = 0; ndev->trans_start = jiffies; } @@ -1015,17 +1277,53 @@ #endif if (!(lp->flags & ISDN_NET_CONNECTED)) { int chi; + /* only do autodial if allowed by config */ + if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) { + isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'"); + dev_kfree_skb(skb, FREE_WRITE); + ndev->tbusy = 0; + return 0; + } if (lp->phone[1]) { ulong flags; save_flags(flags); cli(); + + if(lp->dialwait_timer <= 0) + if(lp->dialstarted > 0 && lp->dialtimeout > 0 && jiffies < lp->dialstarted + lp->dialtimeout + lp->dialwait) + lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait; + + if(lp->dialwait_timer > 0) { + if(jiffies < lp->dialwait_timer) { +/* +printk("reject: jiffies=%ld, started=%ld, timeout=%d, wait=%ld, timer=%ld\n", jiffies, lp->dialstarted, lp->dialtimeout, lp->dialwait, lp->dialwait_timer); +*/ + /* + printk(KERN_WARNING "isdn_net: Dial rejected %s, packet dropped\n", + ndev->name); + */ + isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached"); + dev_kfree_skb(skb, FREE_WRITE); + ndev->tbusy = 0; + restore_flags(flags); + return 0; + } else + lp->dialwait_timer = 0; + } + /* Grab a free ISDN-Channel */ - if ((chi = + if (((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto, lp->l3_proto, lp->pre_device, - lp->pre_channel)) < 0) { + lp->pre_channel)) < 0) && + ((chi = + isdn_get_free_channel(ISDN_USAGE_NET, + lp->l2_proto, + lp->l3_proto, + lp->pre_device, + lp->pre_channel^1)) < 0)) { restore_flags(flags); #if 0 printk(KERN_WARNING @@ -1045,6 +1343,7 @@ /* Log packet, which triggered dialing */ if (dev->net_verbose) isdn_net_log_packet(buf, lp); + lp->dialstate = 1; lp->flags |= ISDN_NET_CONNECTED; /* Connect interface with channel */ @@ -1067,7 +1366,7 @@ * when using encap ETHER */ if (lp->first_skb) { - printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n"); + printk(KERN_DEBUG "isdn_net_start_xmit: First skb already set!\n"); dev_kfree_skb(lp->first_skb, FREE_WRITE); lp->first_skb = NULL; } @@ -1268,7 +1567,8 @@ lp->name); kfree_skb(skb, FREE_READ); return; - } + } + netif_rx(skb); return; } @@ -1346,11 +1646,11 @@ isdn_net_local *lp = dev->priv; ushort len = 0; - skb->mac.raw = skb->data; switch (lp->p_encap) { case ISDN_NET_ENCAP_ETHER: len = my_eth_header(skb, dev, type, daddr, saddr, plen); break; + break; #ifdef CONFIG_ISDN_PPP case ISDN_NET_ENCAP_SYNCPPP: /* stick on a fake header to keep fragmentation code happy. */ @@ -1493,6 +1793,9 @@ ndev->pa_mask = 0; ndev->pa_alen = 4; + /* for clients with MPPP maybe higher values better */ + ndev->tx_queue_len = 30; + for (i = 0; i < ETH_ALEN; i++) ndev->broadcast[i] = 0xff; @@ -1502,7 +1805,6 @@ /* The ISDN-specific entries in the device structure. */ ndev->open = &isdn_net_open; ndev->hard_start_xmit = &isdn_net_start_xmit; - /* * up till binding we ask the protocol layer to reserve as much * as we might need for HL layer @@ -1514,7 +1816,6 @@ max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen; ndev->hard_header_len = ETH_HLEN + max_hlhdr_len; - ndev->stop = &isdn_net_close; ndev->get_stats = &isdn_net_get_stats; ndev->rebuild_header = &isdn_net_rebuild_header; @@ -1648,9 +1949,11 @@ * 4 = Wait cbdelay, then call back */ int -isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) +isdn_net_find_icall(int di, isdn_ctrl *c, int idx) { char *eaz; + int ch = c->arg; + setup_parm *setup = &c->parm.setup; int si1; int si2; int ematch; @@ -1664,36 +1967,37 @@ /* Search name in netdev-chain */ save_flags(flags); cli(); - if (!setup.phone[0]) { + if (!setup->phone[0]) { nr[0] = '0'; nr[1] = '\0'; printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n"); } else - strcpy(nr, setup.phone); - si1 = (int) setup.si1; - si2 = (int) setup.si2; - if (!setup.eazmsn[0]) { + strcpy(nr, setup->phone); + si1 = (int) setup->si1; + si2 = (int) setup->si2; + if (!setup->eazmsn[0]) { printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n"); eaz = "0"; } else - eaz = setup.eazmsn; + eaz = setup->eazmsn; if (dev->net_verbose > 1) printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz); /* Accept only calls with Si1 = 7 (Data-Transmission) */ if (si1 != 7) { if (dev->net_verbose > 1) printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n"); + restore_flags(flags); return 0; } n = (isdn_net_phone *) 0; - p = dev->netdev; ematch = 0; #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx, dev->usage[idx]); #endif swapped = 0; - while (p) { + + for (p = dev->netdev; p; p = (isdn_net_dev *) p->next) { /* If last check has triggered as binding-swap, revert it */ switch (swapped) { case 2: @@ -1704,18 +2008,20 @@ break; } swapped = 0; - if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) - ematch = 1; + if (strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) + continue; /* next loop for next device */ + ematch = 1; /* EAZ matches! */ #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n", p->local.name, p->local.msn, p->local.flags, p->local.dialstate); #endif - if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) && /* EAZ is matching */ - (((!(p->local.flags & ISDN_NET_CONNECTED)) && /* but not connected */ - (USG_NONE(dev->usage[idx]))) || /* and ch. unused or */ - ((((p->local.dialstate == 4) || (p->local.dialstate == 12)) && /* if dialing */ - (!(p->local.flags & ISDN_NET_CALLBACK))) /* but no callback */ - ))) { + if ( (!(p->local.flags & ISDN_NET_CONNECTED) && /* not connected */ + USG_NONE(dev->usage[idx])) /* and ch. unused */ + || /* or */ + (((p->local.dialstate == 4) || (p->local.dialstate == 12) || + (p->local.dialstate == 13)) && /* if dialing */ + !(p->local.flags & ISDN_NET_CALLBACK)) /* but no callback */ + ) /*if*/ { #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n", p->local.pre_device, p->local.pre_channel); @@ -1752,7 +2058,6 @@ swapped = 1; } else { /* ... else iterate next device */ - p = (isdn_net_dev *) p->next; continue; } } else { @@ -1774,7 +2079,6 @@ #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: final check failed\n"); #endif - p = (isdn_net_dev *) p->next; continue; } } @@ -1783,7 +2087,6 @@ #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: already on 2nd channel\n"); #endif - p = (isdn_net_dev *) p->next; continue; } } @@ -1804,12 +2107,26 @@ #ifdef ISDN_DEBUG_NET_ICALL printk(KERN_DEBUG "n_fi: match3\n"); #endif - /* Here we got an interface matched, now see if it is up. + /* matching interface found */ + + /* + * Is the state STOPPED? + * If so, no dialin is allowed, + * so reject actively. + * */ + if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) { + restore_flags(flags); + printk(KERN_INFO "incoming call, interface `%s' dialmode `off' -> rejected\n", + lp->name); + return 3; + } + /* + * Is the interface up? * If not, reject the call actively. */ if (!p->dev.start) { restore_flags(flags); - printk(KERN_INFO "%s: incoming call, if down -> rejected\n", + printk(KERN_INFO "incoming call, interface `%s' down -> rejected\n", lp->name); return 3; } @@ -1820,7 +2137,7 @@ isdn_net_local *mlp = (isdn_net_local *) lp->master->priv; printk(KERN_DEBUG "ICALLslv: %s\n", lp->name); printk(KERN_DEBUG "master=%s\n", mlp->name); - if (mlp->flags & ISDN_NET_CONNECTED) { + if ((mlp->flags & ISDN_NET_CONNECTED) && (!mlp->dialstate)) { printk(KERN_DEBUG "master online\n"); /* Master is online, find parent-slave (master if first slave) */ while (mlp->slave) { @@ -1832,13 +2149,23 @@ printk(KERN_DEBUG "master offline\n"); /* Found parent, if it's offline iterate next device */ printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED); - if (!(mlp->flags & ISDN_NET_CONNECTED)) { - p = (isdn_net_dev *) p->next; + if (!(mlp->flags & ISDN_NET_CONNECTED) || mlp->dialstate) { continue; } } if (lp->flags & ISDN_NET_CALLBACK) { int chi; + /* + * Is the state MANUAL? + * If so, no callback can be made, + * so reject actively. + * */ + if (ISDN_NET_DIALMODE(*lp) != ISDN_NET_DM_AUTO) { + restore_flags(flags); + printk(KERN_INFO "incoming call for callback, interface `%s' dialmode not `auto' -> rejected\n", + lp->name); + return 3; + } printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n", lp->name, nr, eaz); if (lp->phone[1]) { @@ -1877,11 +2204,13 @@ eaz); /* if this interface is dialing, it does it probably on a different device, so free this device */ - if ((p->local.dialstate == 4) || (p->local.dialstate == 12)) { + if (p->local.dialstate && + ((p->local.isdn_device != di) || (p->local.isdn_channel != ch))) { #ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_free(lp); #endif + isdn_net_unbind_ptr(lp->isdn_device, lp->isdn_channel); isdn_free_channel(p->local.isdn_device, p->local.isdn_channel, ISDN_USAGE_NET); } @@ -1913,10 +2242,9 @@ } } } - p = (isdn_net_dev *) p->next; } /* If none of configured EAZ/MSN matched and not verbose, be silent */ - if (ematch || dev->net_verbose) + if (!ematch || dev->net_verbose) printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz); restore_flags(flags); return 0; @@ -2074,10 +2402,15 @@ netdev->local.srobin = &netdev->dev; netdev->local.hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */ netdev->local.onhtime = 10; /* Default hangup-time for saving costs - of those who forget configuring this */ + of those who forget configuring this */ netdev->local.dialmax = 1; - netdev->local.flags = ISDN_NET_CBHUP; /* Hangup before Callback */ + netdev->local.flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL; /* Hangup before Callback, manual dial */ netdev->local.cbdelay = 25; /* Wait 5 secs before Callback */ + netdev->local.dialtimeout = -1; /* Infinite Dial-Timeout */ + netdev->local.dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */ + netdev->local.dialstarted = 0; /* Jiffies of last dial-start */ + netdev->local.dialwait_timer = 0; /* Jiffies of earliest next dial-start */ + /* Put into to netdev-chain */ netdev->next = (void *) dev->netdev; dev->netdev = netdev; @@ -2225,6 +2558,8 @@ p->local.triggercps = cfg->triggercps; p->local.slavedelay = cfg->slavedelay * HZ; p->local.pppbind = cfg->pppbind; + p->local.dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1; + p->local.dialwait = cfg->dialwait * HZ; if (cfg->secure) p->local.flags |= ISDN_NET_SECURE; else @@ -2246,6 +2581,16 @@ p->local.flags &= ~ISDN_NET_CALLBACK; break; } + p->local.flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */ + if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) { + /* old isdnctrl version, where only 0 or 1 is given */ + printk(KERN_WARNING + "Old isdnctrl version detected! Please update.\n"); + p->local.flags |= ISDN_NET_DM_OFF; /* turn on 'off' bit */ + } + else { + p->local.flags |= cfg->dialmode; /* turn on selected bits */ + } if (cfg->chargehup) p->local.hupflags |= ISDN_CHARGEHUP; else @@ -2259,7 +2604,6 @@ p->local.chargeint = cfg->chargeint * HZ; } if (cfg->p_encap != p->local.p_encap) { - /* FIXME: What if there are alias devices too? */ if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) { p->dev.hard_header = NULL; #if (LINUX_VERSION_CODE < 0x02010F) @@ -2286,7 +2630,11 @@ p->dev.hard_header_cache = NULL; #endif p->dev.header_cache_update = NULL; - p->dev.flags = IFF_NOARP | IFF_SOFTHEADERS; + /* + * paul: IFF_SOFTHEADERS was added in + * 2.0.33?? Does this make sense? + */ + p->dev.flags = IFF_NOARP /* | IFF_SOFTHEADERS */; } } } @@ -2324,15 +2672,18 @@ if (p->local.flags & ISDN_NET_CBOUT) cfg->callback = 2; cfg->cbhup = (p->local.flags & ISDN_NET_CBHUP) ? 1 : 0; + cfg->dialmode = p->local.flags & ISDN_NET_DIALMODE_MASK; cfg->chargehup = (p->local.hupflags & 4) ? 1 : 0; cfg->ihup = (p->local.hupflags & 8) ? 1 : 0; cfg->cbdelay = p->local.cbdelay; cfg->dialmax = p->local.dialmax; - cfg->triggercps = p->local.triggercps; + cfg->triggercps = p->local.triggercps; cfg->slavedelay = p->local.slavedelay / HZ; cfg->chargeint = (p->local.hupflags & ISDN_CHARGEHUP) ? (p->local.chargeint / HZ) : 0; cfg->pppbind = p->local.pppbind; + cfg->dialtimeout = p->local.dialtimeout >= 0 ? p->local.dialtimeout / HZ : -1; + cfg->dialwait = p->local.dialwait / HZ; if (p->local.slave) strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name); else @@ -2433,6 +2784,7 @@ else p->local.phone[inout] = n->next; kfree(n); + restore_flags(flags); return 0; } m = n; diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h --- v2.0.35/linux/drivers/isdn/isdn_net.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_net.h Sun Nov 15 10:32:59 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_net.h,v 1.5 1997/02/10 20:12:47 fritz Exp $ +/* $Id: isdn_net.h,v 1.5.2.2 1998/11/05 22:12:09 fritz Exp $ * header for Linux ISDN subsystem, network related functions (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) * @@ -21,6 +21,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_net.h,v $ + * Revision 1.5.2.2 1998/11/05 22:12:09 fritz + * Changed mail-address. + * + * Revision 1.5.2.1 1998/10/25 15:48:29 fritz + * Misc bugfixes and adaptions to new HiSax + * * Revision 1.5 1997/02/10 20:12:47 fritz * Changed interface for reporting incoming calls. * @@ -49,13 +55,13 @@ extern char *isdn_net_newslave(char *); extern int isdn_net_rm(char *); extern int isdn_net_rmall(void); -extern int isdn_net_stat_callback(int, int); +extern int isdn_net_stat_callback(int, isdn_ctrl *); extern int isdn_net_setcfg(isdn_net_ioctl_cfg *); extern int isdn_net_getcfg(isdn_net_ioctl_cfg *); extern int isdn_net_addphone(isdn_net_ioctl_phone *); extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *); extern int isdn_net_delphone(isdn_net_ioctl_phone *); -extern int isdn_net_find_icall(int, int, int, setup_parm); +extern int isdn_net_find_icall(int, isdn_ctrl *, int); extern void isdn_net_hangup(struct device *); extern void isdn_net_dial(void); extern void isdn_net_autohup(void); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c --- v2.0.35/linux/drivers/isdn/isdn_ppp.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_ppp.c Sun Nov 15 10:33:00 1998 @@ -1,4 +1,4 @@ -/* $Id: isdn_ppp.c,v 1.28 1997/06/17 13:05:57 hipp Exp $ +/* $Id: isdn_ppp.c,v 1.28.2.2 1998/11/03 14:31:23 fritz Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * @@ -19,6 +19,14 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_ppp.c,v $ + * Revision 1.28.2.2 1998/11/03 14:31:23 fritz + * Reduced stack usage in various functions. + * Adapted statemachine to work with certified HiSax. + * Some fixes in callback handling. + * + * Revision 1.28.2.1 1998/03/16 09:56:02 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * * Revision 1.28 1997/06/17 13:05:57 hipp * Applied Eric's underflow-patches (slightly modified) * more compression changes (but disabled at the moment) @@ -173,7 +181,7 @@ static void isdn_ppp_free_mpqueue(isdn_net_dev *); #endif -char *isdn_ppp_revision = "$Revision: 1.28 $"; +char *isdn_ppp_revision = "$Revision: 1.28.2.2 $"; static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; static struct isdn_ppp_compressor *ipc_head = NULL; @@ -638,23 +646,28 @@ break; case PPPIOCGCALLINFO: { - struct pppcallinfo pci; - memset((char *) &pci,0,sizeof(struct pppcallinfo)); + struct pppcallinfo *pci; + + if ((pci = (struct pppcallinfo *)kmalloc(sizeof(struct pppcallinfo), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset((char *) pci,0,sizeof(struct pppcallinfo)); if(lp) { - strncpy(pci.local_num,lp->msn,63); + strncpy(pci->local_num,lp->msn,63); if(lp->dial) { - strncpy(pci.remote_num,lp->dial->num,63); + strncpy(pci->remote_num,lp->dial->num,63); } - pci.charge_units = lp->charge; + pci->charge_units = lp->charge; if(lp->outgoing) - pci.calltype = CALLTYPE_OUTGOING; + pci->calltype = CALLTYPE_OUTGOING; else - pci.calltype = CALLTYPE_INCOMING; + pci->calltype = CALLTYPE_INCOMING; if(lp->flags & ISDN_NET_CALLBACK) - pci.calltype |= CALLTYPE_CALLBACK; + pci->calltype |= CALLTYPE_CALLBACK; } - return set_arg((void *)arg,sizeof(struct pppcallinfo),&pci); + r = set_arg((void *)arg,sizeof(struct pppcallinfo),pci); + kfree(pci); + return r; } default: break; @@ -885,7 +898,8 @@ if (lp->isdn_device < 0 || lp->isdn_channel < 0) return 0; - if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 && + if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && + lp->dialstate == 0 && (lp->flags & ISDN_NET_CONNECTED)) { int cnt; struct sk_buff *skb; @@ -901,6 +915,7 @@ printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32); } + if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb)) != count) { if (lp->sav_skb) { dev_kfree_skb(lp->sav_skb, FREE_WRITE); @@ -1264,10 +1279,10 @@ return; } - netif_rx(skb); - /* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */ /* Reset hangup-timer */ lp->huptimer = 0; + netif_rx(skb); + /* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */ return; } @@ -1376,10 +1391,10 @@ */ /* Pull off the fake header we stuck on earlier to keep - * the fragemntation code happy. - * this will break the ISDN_SYNCPPP_READDRESS hack a few lines - * above. So, enabling this is no longer allowed - */ + * the fragemntation code happy. + * this will break the ISDN_SYNCPPP_READDRESS hack a few lines + * above. So, enabling this is no longer allowed + */ skb_pull(skb,IPPP_MAX_HEADER); if (ipt->debug & 0x4) @@ -1424,9 +1439,9 @@ } #endif - /* - * normal or bundle compression - */ + /* + * normal or bundle compression + */ skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0); if (ipt->debug & 0x24) diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_syms.c linux/drivers/isdn/isdn_syms.c --- v2.0.35/linux/drivers/isdn/isdn_syms.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_syms.c Sun Nov 15 10:33:00 1998 @@ -21,7 +21,7 @@ * Added GPL-Header, Id and Log * */ -#include + #include #include diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c --- v2.0.35/linux/drivers/isdn/isdn_tty.c Thu Aug 14 11:19:48 1997 +++ linux/drivers/isdn/isdn_tty.c Sun Nov 15 10:33:00 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_tty.c,v 1.41 1997/05/27 15:17:31 fritz Exp $ +/* $Id: isdn_tty.c,v 1.41.2.11 1998/11/05 22:12:12 fritz Exp $ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,48 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.c,v $ + * Revision 1.41.2.11 1998/11/05 22:12:12 fritz + * Changed mail-address. + * + * Revision 1.41.2.10 1998/11/03 14:31:35 fritz + * Reduced stack usage in various functions. + * Adapted statemachine to work with certified HiSax. + * Some fixes in callback handling. + * + * Revision 1.41.2.9 1998/10/25 15:48:32 fritz + * Misc bugfixes and adaptions to new HiSax + * + * Revision 1.41.2.8 1998/08/22 16:43:07 armin + * Added silence detection in audio receive mode (AT+VSD). + * + * Revision 1.41.2.7 1998/06/07 13:48:08 fritz + * ABC cleanup + * + * Revision 1.41.2.5 1998/04/08 21:42:35 keil + * Blocksize default 1024 + * + * Revision 1.41.2.4 1998/03/19 17:58:55 detabc + * remove 2 debug-messages (no longer needed) bug was fixed + * + * Revision 1.41.2.3 1998/03/07 23:35:20 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * + * Revision 1.41.2.2 1998/03/07 23:02:51 tsbogend + * fixed kernel unaligned traps on Linux/Alpha + * + * Revision 1.41.2.1 1997/08/21 15:56:11 fritz + * Synchronized 2.0.X branch with 2.0.31-pre7 + * * Revision 1.41 1997/05/27 15:17:31 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -199,6 +241,7 @@ #define VBUFX (VBUF/16) #endif + /* Prototypes */ static int isdn_tty_edit_at(const char *, int, modem_info *, int); @@ -223,7 +266,7 @@ static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.41 $"; +char *isdn_tty_revision = "$Revision: 1.41.2.11 $"; #define DLE 0x10 #define ETX 0x03 @@ -308,6 +351,8 @@ r = 0; #ifdef CONFIG_ISDN_AUDIO isdn_audio_eval_dtmf(info); + if ((info->vonline & 1) && (info->emu.vpar[1])) + isdn_audio_eval_silence(info); #endif if ((tty = info->tty)) { if (info->mcr & UART_MCR_RTS) { @@ -364,6 +409,8 @@ if (info->vonline) isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt); + if ((info->vonline & 1) && (info->emu.vpar[1])) + isdn_audio_calc_silence(info, skb->data, skb->len, ifmt); #endif if ((info->online < 2) #ifdef CONFIG_ISDN_AUDIO @@ -798,20 +845,20 @@ cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; cmd.command = ISDN_CMD_CLREAZ; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETEAZ; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL2; info->last_l2 = l2; cmd.arg = info->isdn_channel + (l2 << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; sprintf(cmd.parm.setup.phone, "%s", n); @@ -823,7 +870,7 @@ info->dialing = 1; strcpy(dev->num[i], n); isdn_info_update(); - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); } } @@ -872,7 +919,7 @@ cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_HANGUP; cmd.arg = info->isdn_channel; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); } isdn_all_eaz(info->isdn_driver, info->isdn_channel); info->emu.mdmreg[1] = 0; @@ -1270,7 +1317,7 @@ status = info->lsr; restore_flags(flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - put_user(result, (ulong *) value); + put_user(result, (uint *) value); return 0; } @@ -1294,7 +1341,7 @@ | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); - put_user(result, (ulong *) value); + put_user(result, (uint *) value); return 0; } @@ -1443,7 +1490,6 @@ return error; else return isdn_tty_get_lsr_info(info, (uint *) arg); - default: #ifdef ISDN_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line); @@ -1927,7 +1973,7 @@ #ifdef CONFIG_ISDN_AUDIO skb_queue_head_init(&info->dtmf_queue); #endif - if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) { + if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) { printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); return -3; } @@ -1944,9 +1990,11 @@ * Return Index to dev->mdm or -1 if none found. */ int -isdn_tty_find_icall(int di, int ch, setup_parm setup) +isdn_tty_find_icall(int di, isdn_ctrl *c) { char *eaz; + int ch = c->arg; + setup_parm *setup = &c->parm.setup; int i; int idx; int si1; @@ -1956,19 +2004,19 @@ save_flags(flags); cli(); - if (!setup.phone[0]) { + if (!setup->phone[0]) { nr[0] = '0'; nr[1] = '\0'; printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n"); } else - strcpy(nr, setup.phone); - si1 = (int) setup.si1; - si2 = (int) setup.si2; - if (!setup.eazmsn[0]) { + strcpy(nr, setup->phone); + si1 = (int) setup->si1; + si2 = (int) setup->si2; + if (!setup->eazmsn[0]) { printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n"); eaz = "0"; } else - eaz = setup.eazmsn; + eaz = setup->eazmsn; #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2); #endif @@ -2002,8 +2050,8 @@ dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM; strcpy(dev->num[idx], nr); info->emu.mdmreg[20] = si2bit[si1]; - info->emu.mdmreg[21] = setup.plan; - info->emu.mdmreg[22] = setup.screen; + info->emu.mdmreg[21] = setup->plan; + info->emu.mdmreg[22] = setup->screen; isdn_info_update(); restore_flags(flags); printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr, @@ -2016,7 +2064,7 @@ } } printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz, - dev->drv[di]->reject_bus ? "rejected" : "ignored"); + (dev->drv[di]->flags & DRV_FLAG_REJBUS) ? "rejected" : "ignored"); restore_flags(flags); return -1; } @@ -2534,7 +2582,7 @@ /* &B - Set Buffersize */ p[0]++; i = isdn_getnum(p); - if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE)) + if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX)) PARSE_ERROR1; #ifdef CONFIG_ISDN_AUDIO if ((m->mdmreg[18] & 1) && (i > VBUF)) @@ -2643,7 +2691,7 @@ return 1; break; case 16: - if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE) + if ((mval * 16) > ISDN_SERIAL_XMIT_MAX) return 1; #ifdef CONFIG_ISDN_AUDIO if ((m->mdmreg[18] & 1) && (mval > VBUFX)) @@ -2756,15 +2804,15 @@ cmd.command = ISDN_CMD_SETL2; cmd.arg = info->isdn_channel + (l2 << 8); info->last_l2 = l2; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.command = ISDN_CMD_SETL3; cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8); - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); cmd.driver = info->isdn_driver; cmd.arg = info->isdn_channel; cmd.command = ISDN_CMD_ACCEPTD; - dev->drv[info->isdn_driver]->interface->command(&cmd); + isdn_command(&cmd); } else isdn_tty_modem_result(8, info); } @@ -2937,6 +2985,11 @@ printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); PARSE_ERROR1; } + info->silence_state = isdn_audio_silence_init(info->silence_state); + if (!info->silence_state) { + printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n"); + PARSE_ERROR1; + } if (m->vpar[3] < 5) { info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]); if (!info->adpcmr) { @@ -2962,33 +3015,29 @@ isdn_tty_at_cout(rs, info); break; case '=': - p[0]++; - switch (*p[0]) { - case '0': - case '1': - case '2': - case '3': - par1 = isdn_getnum(p); - if ((par1 < 0) || (par1 > 31)) - PARSE_ERROR1; - if (*p[0] != ',') - PARSE_ERROR1; - p[0]++; - par2 = isdn_getnum(p); - if ((par2 < 0) || (par2 > 255)) - PARSE_ERROR1; - m->vpar[1] = par1; - m->vpar[2] = par2; - break; - case '?': - p[0]++; - isdn_tty_at_cout("\r\n<0-31>,<0-255>", - info); - break; - default: - PARSE_ERROR1; - } - break; + p[0]++; + if ((*p[0]>='0') && (*p[0]<='9')) { + par1 = isdn_getnum(p); + if ((par1 < 0) || (par1 > 31)) + PARSE_ERROR1; + if (*p[0] != ',') + PARSE_ERROR1; + p[0]++; + par2 = isdn_getnum(p); + if ((par2 < 0) || (par2 > 255)) + PARSE_ERROR1; + m->vpar[1] = par1; + m->vpar[2] = par2; + break; + } else + if (*p[0] == '?') { + p[0]++; + isdn_tty_at_cout("\r\n<0-31>,<0-255>", + info); + break; + } else + PARSE_ERROR1; + break; default: PARSE_ERROR1; } @@ -3095,7 +3144,7 @@ break; case 'D': /* D - Dial */ - isdn_tty_getdial(++p, ds, sizeof(ds)); + isdn_tty_getdial(++p, ds,sizeof(ds)); p += strlen(p); if (!strlen(m->msn)) isdn_tty_modem_result(10, info); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h --- v2.0.35/linux/drivers/isdn/isdn_tty.h Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_tty.h Sun Nov 15 10:33:00 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn_tty.h,v 1.10 1997/03/02 14:29:26 fritz Exp $ +/* $Id: isdn_tty.h,v 1.10.2.2 1998/11/05 22:12:25 fritz Exp $ * header for Linux ISDN subsystem, tty related functions (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn_tty.h,v $ + * Revision 1.10.2.2 1998/11/05 22:12:25 fritz + * Changed mail-address. + * + * Revision 1.10.2.1 1998/10/25 15:48:44 fritz + * Misc bugfixes and adaptions to new HiSax + * * Revision 1.10 1997/03/02 14:29:26 fritz * More ttyI related cleanup. * @@ -59,7 +65,7 @@ extern void isdn_tty_modem_xmit(void); extern int isdn_tty_modem_init(void); extern void isdn_tty_readmodem(void); -extern int isdn_tty_find_icall(int, int, setup_parm); +extern int isdn_tty_find_icall(int, isdn_ctrl *); extern void isdn_tty_cleanup_xmit(modem_info *); extern int isdn_tty_stat_callback(int, isdn_ctrl *); extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdnloop/Makefile linux/drivers/isdn/isdnloop/Makefile --- v2.0.35/linux/drivers/isdn/isdnloop/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/isdnloop/Makefile Sun Nov 15 10:33:01 1998 @@ -0,0 +1,11 @@ +L_OBJS := +M_OBJS := + +ifeq ($(CONFIG_ISDN_DRV_LOOP),y) + L_OBJS += isdnloop.o +else + M_OBJS += isdnloop.o +endif + +include $(TOPDIR)/Rules.make + diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdnloop/isdnloop.c linux/drivers/isdn/isdnloop/isdnloop.c --- v2.0.35/linux/drivers/isdn/isdnloop/isdnloop.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/isdnloop/isdnloop.c Sun Nov 15 10:33:01 1998 @@ -0,0 +1,1563 @@ +/* $Id: isdnloop.c,v 1.1.2.1 1998/11/05 22:13:15 fritz Exp $ + + * ISDN low-level module implementing a dummy loop driver. + * + * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.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, 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: isdnloop.c,v $ + * Revision 1.1.2.1 1998/11/05 22:13:15 fritz + * Changed mail-address. + * + * Revision 1.1 1997/03/24 23:02:04 fritz + * Added isdnloop driver. + * + */ + +#include "isdnloop.h" + +static char +*revision = "$Revision: 1.1.2.1 $"; + +static int isdnloop_addcard(char *); + +/* + * Free queue completely. + * + * Parameter: + * card = pointer to card struct + * channel = channel number + */ +static void +isdnloop_free_queue(isdnloop_card * card, int channel) +{ + struct sk_buff_head *queue = &card->bqueue[channel]; + struct sk_buff *skb; + + while ((skb = skb_dequeue(queue))) + dev_kfree_skb(skb, FREE_WRITE); + card->sndcount[channel] = 0; +} + +/* + * Send B-Channel data to another virtual card. + * This routine is called via timer-callback from isdnloop_pollbchan(). + * + * Parameter: + * card = pointer to card struct. + * ch = channel number (0-based) + */ +static void +isdnloop_bchan_send(isdnloop_card * card, int ch) +{ + isdnloop_card *rcard = card->rcard[ch]; + int rch = card->rch[ch]; + struct sk_buff *skb; + isdn_ctrl cmd; + + while (card->sndcount[ch]) { + if ((skb = skb_dequeue(&card->bqueue[ch]))) { + card->sndcount[ch] -= skb->len; + if (rcard) + rcard->interface.rcvcallb_skb(rcard->myid, rch, skb); + cmd.command = ISDN_STAT_BSENT; + cmd.driver = card->myid; + cmd.arg = ch; + card->interface.statcallb(&cmd); + } else + card->sndcount[ch] = 0; + } +} + +/* + * Send/Receive Data to/from the B-Channel. + * This routine is called via timer-callback. + * It schedules itself while any B-Channel is open. + * + * Parameter: + * data = pointer to card struct, set by kernel timer.data + */ +static void +isdnloop_pollbchan(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + unsigned long flags; + + if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE) + isdnloop_bchan_send(card, 0); + if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE) + isdnloop_bchan_send(card, 1); + if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) { + /* schedule b-channel polling again */ + save_flags(flags); + cli(); + card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; + add_timer(&card->rb_timer); + card->flags |= ISDNLOOP_FLAGS_RBTIMER; + restore_flags(flags); + } else + card->flags &= ~ISDNLOOP_FLAGS_RBTIMER; +} + +/* + * Parse ICN-type setup string and fill fields of setup-struct + * with parsed data. + * + * Parameter: + * setup = setup string, format: [caller-id],si1,si2,[called-id] + * cmd = pointer to struct to be filled. + */ +static void +isdnloop_parse_setup(char *setup, isdn_ctrl * cmd) +{ + char *t = setup; + char *s = strpbrk(t, ","); + + *s++ = '\0'; + strncpy(cmd->parm.setup.phone, t, sizeof(cmd->parm.setup.phone)); + s = strpbrk(t = s, ","); + *s++ = '\0'; + if (!strlen(t)) + cmd->parm.setup.si1 = 0; + else + cmd->parm.setup.si1 = simple_strtoul(t, NULL, 10); + s = strpbrk(t = s, ","); + *s++ = '\0'; + if (!strlen(t)) + cmd->parm.setup.si2 = 0; + else + cmd->parm.setup.si2 = + simple_strtoul(t, NULL, 10); + strncpy(cmd->parm.setup.eazmsn, s, sizeof(cmd->parm.setup.eazmsn)); + cmd->parm.setup.plan = 0; + cmd->parm.setup.screen = 0; +} + +typedef struct isdnloop_stat { + char *statstr; + int command; + int action; +} isdnloop_stat; +/* *INDENT-OFF* */ +static isdnloop_stat isdnloop_stat_table[] = +{ + {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ + {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ + {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ + {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */ + {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */ + {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */ + {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */ + {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */ + {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */ + {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */ + {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */ + {"NO D-CHAN", ISDN_STAT_NODCH, 0}, /* No D-channel available */ + {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ + {"E_L2: DATA LIN", ISDN_STAT_BHUP, 8}, /* Layer-2 data link lost */ + {"E_L1: ACTIVATION FAILED", + ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ + {NULL, 0, -1} +}; +/* *INDENT-ON* */ + + +/* + * Parse Status message-strings from virtual card. + * Depending on status, call statcallb for sending messages to upper + * levels. Also set/reset B-Channel active-flags. + * + * Parameter: + * status = status string to parse. + * channel = channel where message comes from. + * card = card where message comes from. + */ +static void +isdnloop_parse_status(u_char * status, int channel, isdnloop_card * card) +{ + isdnloop_stat *s = isdnloop_stat_table; + int action = -1; + isdn_ctrl cmd; + + while (s->statstr) { + if (!strncmp(status, s->statstr, strlen(s->statstr))) { + cmd.command = s->command; + action = s->action; + break; + } + s++; + } + if (action == -1) + return; + cmd.driver = card->myid; + cmd.arg = channel; + switch (action) { + case 1: + /* BCON_x */ + card->flags |= (channel) ? + ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE; + break; + case 2: + /* BDIS_x */ + card->flags &= ~((channel) ? + ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE); + isdnloop_free_queue(card, channel); + break; + case 3: + /* DCAL_I and DSCA_I */ + isdnloop_parse_setup(status + 6, &cmd); + break; + case 4: + /* FCALL */ + sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid); + sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1); + cmd.parm.setup.si1 = 7; + cmd.parm.setup.si2 = 0; + cmd.parm.setup.plan = 0; + cmd.parm.setup.screen = 0; + break; + case 5: + /* CIF */ + strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1); + break; + case 6: + /* AOC */ + sprintf(cmd.parm.num, "%d", + (int) simple_strtoul(status + 7, NULL, 16)); + break; + case 7: + /* CAU */ + status += 3; + if (strlen(status) == 4) + sprintf(cmd.parm.num, "%s%c%c", + status + 2, *status, *(status + 1)); + else + strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1); + break; + case 8: + /* Misc Errors on L1 and L2 */ + card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE; + isdnloop_free_queue(card, 0); + cmd.arg = 0; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_DHUP; + cmd.arg = 0; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_BHUP; + card->flags &= ~ISDNLOOP_FLAGS_B2ACTIVE; + isdnloop_free_queue(card, 1); + cmd.arg = 1; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + cmd.command = ISDN_STAT_DHUP; + cmd.arg = 1; + cmd.driver = card->myid; + break; + } + card->interface.statcallb(&cmd); +} + +/* + * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl + * + * Parameter: + * card = pointer to card struct. + * c = char to store. + */ +static void +isdnloop_putmsg(isdnloop_card * card, unsigned char c) +{ + ulong flags; + + save_flags(flags); + cli(); + *card->msg_buf_write++ = (c == 0xff) ? '\n' : c; + if (card->msg_buf_write == card->msg_buf_read) { + if (++card->msg_buf_read > card->msg_buf_end) + card->msg_buf_read = card->msg_buf; + } + if (card->msg_buf_write > card->msg_buf_end) + card->msg_buf_write = card->msg_buf; + restore_flags(flags); +} + +/* + * Poll a virtual cards message queue. + * If there are new status-replies from the card, copy them to + * ringbuffer for reading on /dev/isdnctrl and call + * isdnloop_parse_status() for processing them. Watch for special + * Firmware bootmessage and parse it, to get the D-Channel protocol. + * If there are B-Channels open, initiate a timer-callback to + * isdnloop_pollbchan(). + * This routine is called periodically via timer interrupt. + * + * Parameter: + * data = pointer to card struct + */ +static void +isdnloop_polldchan(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + struct sk_buff *skb; + int avail; + int left; + u_char c; + int ch; + int flags; + u_char *p; + isdn_ctrl cmd; + + if ((skb = skb_dequeue(&card->dqueue))) + avail = skb->len; + else + avail = 0; + for (left = avail; left > 0; left--) { + c = *skb->data; + skb_pull(skb, 1); + isdnloop_putmsg(card, c); + card->imsg[card->iptr] = c; + if (card->iptr < 59) + card->iptr++; + if (!skb->len) { + avail++; + isdnloop_putmsg(card, '\n'); + card->imsg[card->iptr] = 0; + card->iptr = 0; + if (card->imsg[0] == '0' && card->imsg[1] >= '0' && + card->imsg[1] <= '2' && card->imsg[2] == ';') { + ch = (card->imsg[1] - '0') - 1; + p = &card->imsg[3]; + isdnloop_parse_status(p, ch, card); + } else { + p = card->imsg; + if (!strncmp(p, "DRV1.", 5)) { + printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p); + if (!strncmp(p + 7, "TC", 2)) { + card->ptype = ISDN_PTYPE_1TR6; + card->interface.features |= ISDN_FEATURE_P_1TR6; + printk(KERN_INFO + "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID); + } + if (!strncmp(p + 7, "EC", 2)) { + card->ptype = ISDN_PTYPE_EURO; + card->interface.features |= ISDN_FEATURE_P_EURO; + printk(KERN_INFO + "isdnloop: (%s) Euro-Protocol loaded and running\n", CID); + } + continue; + + } + } + } + } + if (avail) { + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = avail; + card->interface.statcallb(&cmd); + } + if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) + if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) { + /* schedule b-channel polling */ + card->flags |= ISDNLOOP_FLAGS_RBTIMER; + save_flags(flags); + cli(); + del_timer(&card->rb_timer); + card->rb_timer.function = isdnloop_pollbchan; + card->rb_timer.data = (unsigned long) card; + card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD; + add_timer(&card->rb_timer); + restore_flags(flags); + } + /* schedule again */ + save_flags(flags); + cli(); + card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; + add_timer(&card->st_timer); + restore_flags(flags); +} + +/* + * Append a packet to the transmit buffer-queue. + * + * Parameter: + * channel = Number of B-channel + * skb = packet to send. + * card = pointer to card-struct + * Return: + * Number of bytes transferred, -E??? on error + */ +static int +isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card) +{ + int len = skb->len; + unsigned long flags; + struct sk_buff *nskb; + + if (len > 4000) { + printk(KERN_WARNING + "isdnloop: Send packet too large\n"); + return -EINVAL; + } + if (len) { + if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE)) + return 0; + if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE) + return 0; + save_flags(flags); + cli(); + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + skb_queue_tail(&card->bqueue[channel], nskb); + dev_kfree_skb(skb, FREE_WRITE); + } else + len = 0; + card->sndcount[channel] += len; + restore_flags(flags); + } + return len; +} + +/* + * Read the messages from the card's ringbuffer + * + * Parameter: + * buf = pointer to buffer. + * len = number of bytes to read. + * user = flag, 1: called from userlevel 0: called from kernel. + * card = pointer to card struct. + * Return: + * number of bytes actually transferred. + */ +static int +isdnloop_readstatus(u_char * buf, int len, int user, isdnloop_card * card) +{ + int count; + u_char *p; + + for (p = buf, count = 0; count < len; p++, count++) { + if (card->msg_buf_read == card->msg_buf_write) + return count; + if (user) + put_user(*card->msg_buf_read++, p); + else + *p = *card->msg_buf_read++; + if (card->msg_buf_read > card->msg_buf_end) + card->msg_buf_read = card->msg_buf; + } + return count; +} + +/* + * Simulate a card's response by appending it to the cards + * message queue. + * + * Parameter: + * card = pointer to card struct. + * s = pointer to message-string. + * ch = channel: 0 = generic messages, 1 and 2 = D-channel messages. + * Return: + * 0 on success, 1 on memory squeeze. + */ +static int +isdnloop_fake(isdnloop_card * card, char *s, int ch) +{ + struct sk_buff *skb; + int len = strlen(s) + ((ch >= 0) ? 3 : 0); + + if (!(skb = dev_alloc_skb(len))) { + printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n"); + return 1; + } + if (ch >= 0) + sprintf(skb_put(skb, 3), "%02d;", ch); + memcpy(skb_put(skb, strlen(s)), s, strlen(s)); + skb_queue_tail(&card->dqueue, skb); + return 0; +} +/* *INDENT-OFF* */ +static isdnloop_stat isdnloop_cmd_table[] = +{ + {"BCON_R", 0, 1}, /* B-Channel connect */ + {"BDIS_R", 0, 2}, /* B-Channel disconnect */ + {"DDIS_R", 0, 3}, /* D-Channel disconnect */ + {"DCON_R", 0, 16}, /* D-Channel connect */ + {"DSCA_R", 0, 4}, /* Dial 1TR6-SPV */ + {"DCAL_R", 0, 5}, /* Dial */ + {"EAZC", 0, 6}, /* Clear EAZ listener */ + {"EAZ", 0, 7}, /* Set EAZ listener */ + {"SEEAZ", 0, 8}, /* Get EAZ listener */ + {"MSN", 0, 9}, /* Set/Clear MSN listener */ + {"MSALL", 0, 10}, /* Set multi MSN listeners */ + {"SETSIL", 0, 11}, /* Set SI list */ + {"SEESIL", 0, 12}, /* Get SI list */ + {"SILC", 0, 13}, /* Clear SI list */ + {"LOCK", 0, -1}, /* LOCK channel */ + {"UNLOCK", 0, -1}, /* UNLOCK channel */ + {"FV2ON", 1, 14}, /* Leased mode on */ + {"FV2OFF", 1, 15}, /* Leased mode off */ + {NULL, 0, -1} +}; +/* *INDENT-ON* */ + + +/* + * Simulate an error-response from a card. + * + * Parameter: + * card = pointer to card struct. + */ +static void +isdnloop_fake_err(isdnloop_card * card) +{ + char buf[60]; + + sprintf(buf, "E%s", card->omsg); + isdnloop_fake(card, buf, -1); + isdnloop_fake(card, "NAK", -1); +} + +static u_char ctable_eu[] = +{0x00, 0x11, 0x01, 0x12}; +static u_char ctable_1t[] = +{0x00, 0x3b, 0x01, 0x3a}; + +/* + * Assemble a simplified cause message depending on the + * D-channel protocol used. + * + * Parameter: + * card = pointer to card struct. + * loc = location: 0 = local, 1 = remote. + * cau = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding. + * Return: + * Pointer to buffer containing the assembled message. + */ +static char * +isdnloop_unicause(isdnloop_card * card, int loc, int cau) +{ + static char buf[6]; + + switch (card->ptype) { + case ISDN_PTYPE_EURO: + sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]); + break; + case ISDN_PTYPE_1TR6: + sprintf(buf, "%02X44", ctable_1t[cau]); + break; + default: + return ("0000"); + } + return (buf); +} + +/* + * Release a virtual connection. Called from timer interrupt, when + * called party did not respond. + * + * Parameter: + * card = pointer to card struct. + * ch = channel (0-based) + */ +static void +isdnloop_atimeout(isdnloop_card * card, int ch) +{ + unsigned long flags; + char buf[60]; + + save_flags(flags); + cli(); + if (card->rcard) { + isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1); + card->rcard[ch]->rcard[card->rch[ch]] = NULL; + card->rcard[ch] = NULL; + } + isdnloop_fake(card, "DDIS_I", ch + 1); + /* No user responding */ + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3)); + isdnloop_fake(card, buf, ch + 1); + restore_flags(flags); +} + +/* + * Wrapper for isdnloop_atimeout(). + */ +static void +isdnloop_atimeout0(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + isdnloop_atimeout(card, 0); +} + +/* + * Wrapper for isdnloop_atimeout(). + */ +static void +isdnloop_atimeout1(unsigned long data) +{ + isdnloop_card *card = (isdnloop_card *) data; + isdnloop_atimeout(card, 1); +} + +/* + * Install a watchdog for a user, not responding. + * + * Parameter: + * card = pointer to card struct. + * ch = channel to watch for. + */ +static void +isdnloop_start_ctimer(isdnloop_card * card, int ch) +{ + unsigned long flags; + + save_flags(flags); + cli(); + init_timer(&card->c_timer[ch]); + card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT; + if (ch) + card->c_timer[ch].function = isdnloop_atimeout1; + else + card->c_timer[ch].function = isdnloop_atimeout0; + card->c_timer[ch].data = (unsigned long) card; + add_timer(&card->c_timer[ch]); + restore_flags(flags); +} + +/* + * Kill a pending channel watchdog. + * + * Parameter: + * card = pointer to card struct. + * ch = channel (0-based). + */ +static void +isdnloop_kill_ctimer(isdnloop_card * card, int ch) +{ + unsigned long flags; + + save_flags(flags); + cli(); + del_timer(&card->c_timer[ch]); + restore_flags(flags); +} + +static u_char si2bit[] = +{0, 1, 0, 0, 0, 2, 0, 4, 0, 0}; +static u_char bit2si[] = +{1, 5, 7}; + +/* + * Try finding a listener for an outgoing call. + * + * Parameter: + * card = pointer to calling card. + * p = pointer to ICN-type setup-string. + * lch = channel of calling card. + * cmd = pointer to struct to be filled when parsing setup. + * Return: + * 0 = found match, alerting should happen. + * 1 = found matching number but it is busy. + * 2 = no matching listener. + * 3 = found matching number but SI does not match. + */ +static int +isdnloop_try_call(isdnloop_card * card, char *p, int lch, isdn_ctrl * cmd) +{ + isdnloop_card *cc = cards; + unsigned long flags; + int ch; + int num_match; + int i; + char *e; + char nbuf[32]; + + isdnloop_parse_setup(p, cmd); + while (cc) { + for (ch = 0; ch < 2; ch++) { + /* Exclude ourself */ + if ((cc == card) && (ch == lch)) + continue; + num_match = 0; + switch (cc->ptype) { + case ISDN_PTYPE_EURO: + for (i = 0; i < 3; i++) + if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone))) + num_match = 1; + break; + case ISDN_PTYPE_1TR6: + e = cc->eazlist[ch]; + while (*e) { + sprintf(nbuf, "%s%c", cc->s0num[0], *e); + if (!(strcmp(nbuf, cmd->parm.setup.phone))) + num_match = 1; + e++; + } + } + if (num_match) { + save_flags(flags); + cli(); + /* channel idle? */ + if (!(cc->rcard[ch])) { + /* Check SI */ + if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) { + restore_flags(flags); + return 3; + } + /* ch is idle, si and number matches */ + cc->rcard[ch] = card; + cc->rch[ch] = lch; + card->rcard[lch] = cc; + card->rch[lch] = ch; + restore_flags(flags); + return 0; + } else { + restore_flags(flags); + /* num matches, but busy */ + if (ch == 1) + return 1; + } + } + } + cc = cc->next; + } + return 2; +} + +/* + * Depending on D-channel protocol and caller/called, modify + * phone number. + * + * Parameter: + * card = pointer to card struct. + * phone = pointer phone number. + * caller = flag: 1 = caller, 0 = called. + * Return: + * pointer to new phone number. + */ +static char * +isdnloop_vstphone(isdnloop_card * card, char *phone, int caller) +{ + int i; + static char nphone[30]; + + switch (card->ptype) { + case ISDN_PTYPE_EURO: + if (caller) { + for (i = 0; i < 2; i++) + if (!(strcmp(card->s0num[i], phone))) + return (phone); + return (card->s0num[0]); + } + return (phone); + break; + case ISDN_PTYPE_1TR6: + if (caller) { + sprintf(nphone, "%s%c", card->s0num[0], phone[0]); + return (nphone); + } else + return (&phone[strlen(phone) - 1]); + break; + } + return ("\0"); +} + +/* + * Parse an ICN-type command string sent to the 'card'. + * Perform misc. actions depending on the command. + * + * Parameter: + * card = pointer to card struct. + */ +static void +isdnloop_parse_cmd(isdnloop_card * card) +{ + char *p = card->omsg; + isdn_ctrl cmd; + char buf[60]; + isdnloop_stat *s = isdnloop_cmd_table; + int action = -1; + int i; + int ch; + + if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) { + isdnloop_fake_err(card); + return; + } + ch = card->omsg[1] - '0'; + if ((ch < 0) || (ch > 2)) { + isdnloop_fake_err(card); + return; + } + p += 3; + while (s->statstr) { + if (!strncmp(p, s->statstr, strlen(s->statstr))) { + action = s->action; + if (s->command && (ch != 0)) { + isdnloop_fake_err(card); + return; + } + break; + } + s++; + } + if (action == -1) + return; + switch (action) { + case 1: + /* 0x;BCON_R */ + if (card->rcard[ch - 1]) { + isdnloop_fake(card, "BCON_C", ch); + isdnloop_fake(card->rcard[ch - 1], "BCON_I", + card->rch[ch - 1] + 1); + } + break; + case 2: + /* 0x;BDIS_R */ + isdnloop_fake(card, "BDIS_C", ch); + if (card->rcard[ch - 1]) { + isdnloop_fake(card->rcard[ch - 1], "BDIS_I", + card->rch[ch - 1] + 1); + } + break; + case 16: + /* 0x;DCON_R */ + isdnloop_kill_ctimer(card, ch - 1); + if (card->rcard[ch - 1]) { + isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); + isdnloop_fake(card->rcard[ch - 1], "DCON_C", + card->rch[ch - 1] + 1); + isdnloop_fake(card, "DCON_C", ch); + } + break; + case 3: + /* 0x;DDIS_R */ + isdnloop_kill_ctimer(card, ch - 1); + if (card->rcard[ch - 1]) { + isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]); + isdnloop_fake(card->rcard[ch - 1], "DDIS_I", + card->rch[ch - 1] + 1); + card->rcard[ch - 1] = NULL; + } + isdnloop_fake(card, "DDIS_C", ch); + break; + case 4: + /* 0x;DSCA_Rdd,yy,zz,oo */ + if (card->ptype != ISDN_PTYPE_1TR6) { + isdnloop_fake_err(card); + return; + } + /* Fall through */ + case 5: + /* 0x;DCAL_Rdd,yy,zz,oo */ + p += 6; + switch (isdnloop_try_call(card, p, ch - 1, &cmd)) { + case 0: + /* Alerting */ + sprintf(buf, "D%s_I%s,%02d,%02d,%s", + (action == 4) ? "SCA" : "CAL", + isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1), + cmd.parm.setup.si1, + cmd.parm.setup.si2, + isdnloop_vstphone(card->rcard[ch], + cmd.parm.setup.phone, 0)); + isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1); + /* Fall through */ + case 3: + /* si1 does not match, dont alert but start timer */ + isdnloop_start_ctimer(card, ch - 1); + break; + case 1: + /* Remote busy */ + isdnloop_fake(card, "DDIS_I", ch); + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1)); + isdnloop_fake(card, buf, ch); + break; + case 2: + /* No such user */ + isdnloop_fake(card, "DDIS_I", ch); + sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2)); + isdnloop_fake(card, buf, ch); + break; + } + break; + case 6: + /* 0x;EAZC */ + card->eazlist[ch - 1][0] = '\0'; + break; + case 7: + /* 0x;EAZ */ + p += 3; + strcpy(card->eazlist[ch - 1], p); + break; + case 8: + /* 0x;SEEAZ */ + sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]); + isdnloop_fake(card, buf, ch + 1); + break; + case 9: + /* 0x;MSN */ + break; + case 10: + /* 0x;MSNALL */ + break; + case 11: + /* 0x;SETSIL */ + p += 6; + i = 0; + while (strchr("0157", *p)) { + if (i) + card->sil[ch - 1] |= si2bit[*p - '0']; + i = (*p++ == '0'); + } + if (*p) + isdnloop_fake_err(card); + break; + case 12: + /* 0x;SEESIL */ + sprintf(buf, "SIN-LIST: "); + p = buf + 10; + for (i = 0; i < 3; i++) + if (card->sil[ch - 1] & (1 << i)) + p += sprintf(p, "%02d", bit2si[i]); + isdnloop_fake(card, buf, ch + 1); + break; + case 13: + /* 0x;SILC */ + card->sil[ch - 1] = 0; + break; + case 14: + /* 00;FV2ON */ + break; + case 15: + /* 00;FV2OFF */ + break; + } +} + +/* + * Put command-strings into the of the 'card'. In reality, execute them + * right in place by calling isdnloop_parse_cmd(). Also copy every + * command to the read message ringbuffer, preceeding it with a '>'. + * These mesagges can be read at /dev/isdnctrl. + * + * Parameter: + * buf = pointer to command buffer. + * len = length of buffer data. + * user = flag: 1 = called form userlevel, 0 called from kernel. + * card = pointer to card struct. + * Return: + * number of bytes transfered (currently always equals len). + */ +static int +isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card) +{ + int xcount = 0; + int ocount = 1; + isdn_ctrl cmd; + + while (len) { + int count = MIN(255, len); + u_char *p; + u_char msg[0x100]; + + if (user) + copy_from_user(msg, buf, count); + else + memcpy(msg, buf, count); + isdnloop_putmsg(card, '>'); + for (p = msg; count > 0; count--, p++) { + len--; + xcount++; + isdnloop_putmsg(card, *p); + card->omsg[card->optr] = *p; + if (*p == '\n') { + card->omsg[card->optr] = '\0'; + card->optr = 0; + isdnloop_parse_cmd(card); + if (len) { + isdnloop_putmsg(card, '>'); + ocount++; + } + } else { + if (card->optr < 59) + card->optr++; + } + ocount++; + } + } + cmd.command = ISDN_STAT_STAVAIL; + cmd.driver = card->myid; + cmd.arg = ocount; + card->interface.statcallb(&cmd); + return xcount; +} + +/* + * Delete card's pending timers, send STOP to linklevel + */ +static void +isdnloop_stopcard(isdnloop_card * card) +{ + unsigned long flags; + isdn_ctrl cmd; + + save_flags(flags); + cli(); + if (card->flags & ISDNLOOP_FLAGS_RUNNING) { + card->flags &= ~ISDNLOOP_FLAGS_RUNNING; + del_timer(&card->st_timer); + del_timer(&card->rb_timer); + del_timer(&card->c_timer[0]); + del_timer(&card->c_timer[1]); + cmd.command = ISDN_STAT_STOP; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + } + restore_flags(flags); +} + +/* + * Stop all cards before unload. + */ +static void +isdnloop_stopallcards(void) +{ + isdnloop_card *p = cards; + + while (p) { + isdnloop_stopcard(p); + p = p->next; + } +} + +/* + * Start a 'card'. Simulate card's boot message and set the phone + * number(s) of the virtual 'S0-Interface'. Install D-channel + * poll timer. + * + * Parameter: + * card = pointer to card struct. + * sdefp = pointer to struct holding ioctl parameters. + * Return: + * 0 on success, -E??? otherwise. + */ +static int +isdnloop_start(isdnloop_card * card, isdnloop_sdef * sdefp) +{ + unsigned long flags; + isdnloop_sdef sdef; + int i; + + if (card->flags & ISDNLOOP_FLAGS_RUNNING) + return -EBUSY; + copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef)); + save_flags(flags); + cli(); + switch (sdef.ptype) { + case ISDN_PTYPE_EURO: + if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96", + -1)) { + restore_flags(flags); + return -ENOMEM; + } + card->sil[0] = card->sil[1] = 4; + if (isdnloop_fake(card, "TEI OK", 0)) { + restore_flags(flags); + return -ENOMEM; + } + for (i = 0; i < 3; i++) + strcpy(card->s0num[i], sdef.num[i]); + break; + case ISDN_PTYPE_1TR6: + if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95", + -1)) { + restore_flags(flags); + return -ENOMEM; + } + card->sil[0] = card->sil[1] = 4; + if (isdnloop_fake(card, "TEI OK", 0)) { + restore_flags(flags); + return -ENOMEM; + } + strcpy(card->s0num[0], sdef.num[0]); + card->s0num[1][0] = '\0'; + card->s0num[2][0] = '\0'; + break; + default: + restore_flags(flags); + printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n", + sdef.ptype); + return -EINVAL; + } + init_timer(&card->st_timer); + card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD; + card->st_timer.function = isdnloop_polldchan; + card->st_timer.data = (unsigned long) card; + add_timer(&card->st_timer); + card->flags |= ISDNLOOP_FLAGS_RUNNING; + restore_flags(flags); + return 0; +} + +/* + * Main handler for commands sent by linklevel. + */ +static int +isdnloop_command(isdn_ctrl * c, isdnloop_card * card) +{ + ulong a; + int i; + char cbuf[60]; + isdn_ctrl cmd; + isdnloop_cdef cdef; + + switch (c->command) { + case ISDN_CMD_IOCTL: + memcpy(&a, c->parm.num, sizeof(ulong)); + switch (c->arg) { + case ISDNLOOP_IOCTL_DEBUGVAR: + return (ulong) card; + case ISDNLOOP_IOCTL_STARTUP: + if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef)))) + return i; + return (isdnloop_start(card, (isdnloop_sdef *) a)); + break; + case ISDNLOOP_IOCTL_ADDCARD: + if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_cdef)))) + return i; + copy_from_user((char *) &cdef, (char *) a, sizeof(cdef)); + return (isdnloop_addcard(cdef.id1)); + break; + case ISDNLOOP_IOCTL_LEASEDCFG: + if (a) { + if (!card->leased) { + card->leased = 1; + while (card->ptype == ISDN_PTYPE_UNKNOWN) { + current->timeout = jiffies + 10; + schedule(); + } + current->timeout = jiffies + 10; + schedule(); + sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "isdnloop: (%s) Leased-line mode enabled\n", + CID); + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + card->interface.statcallb(&cmd); + } + } else { + if (card->leased) { + card->leased = 0; + sprintf(cbuf, "00;FV2OFF\n"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + printk(KERN_INFO + "isdnloop: (%s) Leased-line mode disabled\n", + CID); + cmd.command = ISDN_STAT_RUN; + cmd.driver = card->myid; + cmd.arg = 0; + card->interface.statcallb(&cmd); + } + } + return 0; + default: + return -EINVAL; + } + break; + case ISDN_CMD_DIAL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if ((c->arg & 255) < ISDNLOOP_BCH) { + char *p; + char dial[50]; + char dcode[4]; + + a = c->arg; + p = c->parm.setup.phone; + if (*p == 's' || *p == 'S') { + /* Dial for SPV */ + p++; + strcpy(dcode, "SCA"); + } else + /* Normal Dial */ + strcpy(dcode, "CAL"); + strcpy(dial, p); + sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), + dcode, dial, c->parm.setup.si1, + c->parm.setup.si2, c->parm.setup.eazmsn); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTD: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + cbuf[0] = 0; + switch (card->l2_proto[a - 1]) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BX75\n", (int) a); + break; + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BTRA\n", (int) a); + break; + } + if (strlen(cbuf)) + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + sprintf(cbuf, "%02d;DCON_R\n", (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_ACCEPTB: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + switch (card->l2_proto[a - 1]) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a); + break; + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a); + break; + default: + sprintf(cbuf, "%02d;BCON_R\n", (int) a); + } + printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + break; + case ISDN_CMD_HANGUP: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETEAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + if (card->ptype == ISDN_PTYPE_EURO) { + sprintf(cbuf, "%02d;MS%s%s\n", (int) a, + c->parm.num[0] ? "N" : "ALL", c->parm.num); + } else + sprintf(cbuf, "%02d;EAZ%s\n", (int) a, + c->parm.num[0] ? c->parm.num : "0123456789"); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_CLREAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if (card->leased) + break; + if (c->arg < ISDNLOOP_BCH) { + a = c->arg + 1; + if (card->ptype == ISDN_PTYPE_EURO) + sprintf(cbuf, "%02d;MSNC\n", (int) a); + else + sprintf(cbuf, "%02d;EAZC\n", (int) a); + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + } + break; + case ISDN_CMD_SETL2: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) { + a = c->arg; + switch (a >> 8) { + case ISDN_PROTO_L2_X75I: + sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1); + break; + case ISDN_PROTO_L2_HDLC: + sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1); + break; + default: + return -EINVAL; + } + i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card); + card->l2_proto[a & 255] = (a >> 8); + } + break; + case ISDN_CMD_GETL2: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) + return card->l2_proto[c->arg & 255]; + else + return -ENODEV; + case ISDN_CMD_SETL3: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return 0; + case ISDN_CMD_GETL3: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + if ((c->arg & 255) < ISDNLOOP_BCH) + return ISDN_PROTO_L3_TRANS; + else + return -ENODEV; + case ISDN_CMD_GETEAZ: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_SETSIL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_GETSIL: + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + break; + case ISDN_CMD_LOCK: + MOD_INC_USE_COUNT; + break; + case ISDN_CMD_UNLOCK: + MOD_DEC_USE_COUNT; + break; + default: + return -EINVAL; + } + } + return 0; +} + +/* + * Find card with given driverId + */ +static inline isdnloop_card * +isdnloop_findcard(int driverid) +{ + isdnloop_card *p = cards; + + while (p) { + if (p->myid == driverid) + return p; + p = p->next; + } + return (isdnloop_card *) 0; +} + +/* + * Wrapper functions for interface to linklevel + */ +static int +if_command(isdn_ctrl * c) +{ + isdnloop_card *card = isdnloop_findcard(c->driver); + + if (card) + return (isdnloop_command(c, card)); + printk(KERN_ERR + "isdnloop: if_command called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_writecmd(const u_char * buf, int len, int user, int id, int channel) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_writecmd(buf, len, user, card)); + } + printk(KERN_ERR + "isdnloop: if_writecmd called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_readstatus(u_char * buf, int len, int user, int id, int channel) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_readstatus(buf, len, user, card)); + } + printk(KERN_ERR + "isdnloop: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +static int +if_sendbuf(int id, int channel, struct sk_buff *skb) +{ + isdnloop_card *card = isdnloop_findcard(id); + + if (card) { + if (!card->flags & ISDNLOOP_FLAGS_RUNNING) + return -ENODEV; + return (isdnloop_sendbuf(channel, skb, card)); + } + printk(KERN_ERR + "isdnloop: if_readstatus called with invalid driverId!\n"); + return -ENODEV; +} + +/* + * Allocate a new card-struct, initialize it + * link it into cards-list and register it at linklevel. + */ +static isdnloop_card * +isdnloop_initcard(char *id) +{ + isdnloop_card *card; + int i; + + if (!(card = (isdnloop_card *) kmalloc(sizeof(isdnloop_card), GFP_KERNEL))) { + printk(KERN_WARNING + "isdnloop: (%s) Could not allocate card-struct.\n", id); + return (isdnloop_card *) 0; + } + memset((char *) card, 0, sizeof(isdnloop_card)); + card->interface.channels = ISDNLOOP_BCH; + card->interface.maxbufsize = 4000; + card->interface.command = if_command; + card->interface.writebuf_skb = if_sendbuf; + card->interface.writecmd = if_writecmd; + card->interface.readstat = if_readstatus; + card->interface.features = ISDN_FEATURE_L2_X75I | + ISDN_FEATURE_L2_HDLC | + ISDN_FEATURE_L3_TRANS | + ISDN_FEATURE_P_UNKNOWN; + card->ptype = ISDN_PTYPE_UNKNOWN; + strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); + card->msg_buf_write = card->msg_buf; + card->msg_buf_read = card->msg_buf; + card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1]; + for (i = 0; i < ISDNLOOP_BCH; i++) { + card->l2_proto[i] = ISDN_PROTO_L2_X75I; + skb_queue_head_init(&card->bqueue[i]); + } + skb_queue_head_init(&card->dqueue); + card->next = cards; + cards = card; + if (!register_isdn(&card->interface)) { + cards = cards->next; + printk(KERN_WARNING + "isdnloop: Unable to register %s\n", id); + kfree(card); + return (isdnloop_card *) 0; + } + card->myid = card->interface.channels; + return card; +} + +static int +isdnloop_addcard(char *id1) +{ + ulong flags; + isdnloop_card *card; + + save_flags(flags); + cli(); + if (!(card = isdnloop_initcard(id1))) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + printk(KERN_INFO + "isdnloop: (%s) virtual card added\n", + card->interface.id); + return 0; +} + +#ifdef MODULE +#define isdnloop_init init_module +#else +void +isdnloop_setup(char *str, int *ints) +{ + static char sid[20]; + + if (strlen(str)) { + strcpy(sid, str); + isdnloop_id = sid; + } +} +#endif + +int +isdnloop_init(void) +{ + char *p; + char rev[10]; + + /* No symbols to export, hide all symbols */ +#if (LINUX_VERSION_CODE < 0x020111) + register_symtab(NULL); +#else + EXPORT_NO_SYMBOLS; +#endif + + if ((p = strchr(revision, ':'))) { + strcpy(rev, p + 1); + p = strchr(rev, '$'); + *p = 0; + } else + strcpy(rev, " ??? "); + printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev); + return (isdnloop_addcard(isdnloop_id)); +} + +#ifdef MODULE +void +cleanup_module(void) +{ + isdn_ctrl cmd; + isdnloop_card *card = cards; + isdnloop_card *last; + int i; + + isdnloop_stopallcards(); + while (card) { + cmd.command = ISDN_STAT_UNLOAD; + cmd.driver = card->myid; + card->interface.statcallb(&cmd); + for (i = 0; i < ISDNLOOP_BCH; i++) + isdnloop_free_queue(card, i); + card = card->next; + } + card = cards; + while (card) { + struct sk_buff *skb; + + last = card; + while ((skb = skb_dequeue(&card->dqueue))) + dev_kfree_skb(skb, FREE_WRITE); + card = card->next; + kfree(last); + } + printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n"); +} +#endif diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdnloop/isdnloop.h linux/drivers/isdn/isdnloop/isdnloop.h --- v2.0.35/linux/drivers/isdn/isdnloop/isdnloop.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/isdn/isdnloop/isdnloop.h Sun Nov 15 10:33:01 1998 @@ -0,0 +1,145 @@ +/* $Id: isdnloop.h,v 1.1.2.1 1998/11/05 22:13:20 fritz Exp $ + + * Loopback lowlevel module for testing of linklevel. + * + * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.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, 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: isdnloop.h,v $ + * Revision 1.1.2.1 1998/11/05 22:13:20 fritz + * Changed mail-address. + * + * Revision 1.1 1997/03/24 23:02:05 fritz + * Added isdnloop driver. + * + */ + +#ifndef isdnloop_h +#define isdnloop_h + +#define ISDNLOOP_IOCTL_DEBUGVAR 0 +#define ISDNLOOP_IOCTL_ADDCARD 1 +#define ISDNLOOP_IOCTL_LEASEDCFG 2 +#define ISDNLOOP_IOCTL_STARTUP 3 + +/* Struct for adding new cards */ +typedef struct isdnloop_cdef { + char id1[10]; +} isdnloop_cdef; + +/* Struct for configuring cards */ +typedef struct isdnloop_sdef { + int ptype; + char num[3][20]; +} isdnloop_sdef; + +#if defined(__KERNEL__) || defined(__DEBUGVAR__) + +#ifdef __KERNEL__ +/* Kernel includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* __KERNEL__ */ + +#define ISDNLOOP_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */ +#define ISDNLOOP_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */ +#define ISDNLOOP_FLAGS_RUNNING 4 /* Cards driver activated */ +#define ISDNLOOP_FLAGS_RBTIMER 8 /* scheduling of B-Channel-poll */ +#define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle */ +#define ISDNLOOP_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */ +#define ISDNLOOP_TIMER_ALERTWAIT (10*HZ) /* Alert timeout */ +#define ISDNLOOP_MAX_SQUEUE 65536 /* Max. outstanding send-data */ +#define ISDNLOOP_BCH 2 /* channels per card */ + +/* + * Per card driver data + */ +typedef struct isdnloop_card { + struct isdnloop_card *next; /* Pointer to next device struct */ + struct isdnloop_card + *rcard[ISDNLOOP_BCH]; /* Pointer to 'remote' card */ + int rch[ISDNLOOP_BCH]; /* 'remote' channel */ + int myid; /* Driver-Nr. assigned by linklevel */ + int leased; /* Flag: This Adapter is connected */ + /* to a leased line */ + int sil[ISDNLOOP_BCH]; /* SI's to listen for */ + char eazlist[ISDNLOOP_BCH][11]; + /* EAZ's to listen for */ + char s0num[3][20]; /* 1TR6 base-number or MSN's */ + unsigned short flags; /* Statusflags */ + int ptype; /* Protocol type (1TR6 or Euro) */ + struct timer_list st_timer; /* Timer for Status-Polls */ + struct timer_list rb_timer; /* Timer for B-Channel-Polls */ + struct timer_list + c_timer[ISDNLOOP_BCH]; /* Timer for Alerting */ + int l2_proto[ISDNLOOP_BCH]; /* Current layer-2-protocol */ + isdn_if interface; /* Interface to upper layer */ + int iptr; /* Index to imsg-buffer */ + char imsg[60]; /* Internal buf for status-parsing */ + int optr; /* Index to omsg-buffer */ + char omsg[60]; /* Internal buf for cmd-parsing */ + char msg_buf[2048]; /* Buffer for status-messages */ + char *msg_buf_write; /* Writepointer for statusbuffer */ + char *msg_buf_read; /* Readpointer for statusbuffer */ + char *msg_buf_end; /* Pointer to end of statusbuffer */ + int sndcount[ISDNLOOP_BCH]; /* Byte-counters for B-Ch.-send */ + struct sk_buff_head + bqueue[ISDNLOOP_BCH]; /* B-Channel queues */ + struct sk_buff_head dqueue; /* D-Channel queue */ +} isdnloop_card; + +/* + * Main driver data + */ +#ifdef __KERNEL__ +static isdnloop_card *cards = (isdnloop_card *) 0; +static char *isdnloop_id = "\0"; + +#ifdef MODULE +#if (LINUX_VERSION_CODE > 0x020111) +MODULE_AUTHOR("Fritz Elfert"); +MODULE_PARM(isdnloop_id, "s"); +MODULE_PARM_DESC(isdnloop_id, "ID-String of first card"); +#endif +#endif + +#endif /* __KERNEL__ */ + +/* Utility-Macros */ + +#define CID (card->interface.id) +#define MIN(a,b) ((ab)?a:b) + +#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ +#endif /* isdnloop_h */ diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/pcbit/callbacks.c linux/drivers/isdn/pcbit/callbacks.c --- v2.0.35/linux/drivers/isdn/pcbit/callbacks.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/pcbit/callbacks.c Sun Nov 15 10:33:01 1998 @@ -164,12 +164,18 @@ * ictl.num >= strlen() + strlen() + 5 */ + if (cbdata->data.setup.CallingPN == NULL) + strcpy(ictl.parm.setup.phone, "0"); + else strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN); + if (cbdata->data.setup.CalledPN == NULL) + strcpy(ictl.parm.setup.eazmsn, "0"); + else strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN); - ictl.parm.setup.si1 = 7; - ictl.parm.setup.si2 = 0; - ictl.parm.setup.plan = 0; - ictl.parm.setup.screen = 0; + ictl.parm.setup.si1 = 7; + ictl.parm.setup.si2 = 0; + ictl.parm.setup.plan = 0; + ictl.parm.setup.screen = 0; #ifdef DEBUG printk(KERN_DEBUG "statstr: %s\n", ictl.num); diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/pcbit/pcbit.h linux/drivers/isdn/pcbit/pcbit.h --- v2.0.35/linux/drivers/isdn/pcbit/pcbit.h Tue Apr 23 02:31:35 1996 +++ linux/drivers/isdn/pcbit/pcbit.h Sun Nov 15 10:33:01 1998 @@ -98,7 +98,7 @@ }; #define STATS_TIMER (10*HZ) -#define ERRTIME (0.1*HZ) +#define ERRTIME (HZ/10) /* MRU */ #define MAXBUFSIZE 1534 diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c --- v2.0.35/linux/drivers/isdn/sc/interrupt.c Sun Nov 15 10:49:35 1998 +++ linux/drivers/isdn/sc/interrupt.c Sun Nov 15 10:33:01 1998 @@ -1,5 +1,5 @@ /* - * $Id: interrupt.c,v 1.3 1997/02/11 22:53:43 fritz Exp $ + * $Id: interrupt.c,v 1.3.2.1 1998/08/03 15:52:31 paul Exp $ * Copyright (C) 1996 SpellCaster Telecommunications Inc. * * This program is free software; you can redistribute it and/or modify diff -u --recursive --new-file v2.0.35/linux/drivers/net/3c505.h linux/drivers/net/3c505.h --- v2.0.35/linux/drivers/net/3c505.h Tue Aug 15 08:08:32 1995 +++ linux/drivers/net/3c505.h Sun Nov 15 10:33:01 1998 @@ -61,7 +61,7 @@ #define DMA_BRST 0x01 /* DMA burst */ /* - * maximum amount of data data allowed in a PCB + * maximum amount of data allowed in a PCB */ #define MAX_PCB_DATA 62 diff -u --recursive --new-file v2.0.35/linux/drivers/net/3c509.c linux/drivers/net/3c509.c --- v2.0.35/linux/drivers/net/3c509.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/3c509.c Sun Nov 15 10:33:01 1998 @@ -1,8 +1,8 @@ /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ /* - Written 1993-1997 by Donald Becker. + Written 1993-1998 by Donald Becker. - Copyright 1994-1997 by Donald Becker. + Copyright 1994-1998 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, @@ -33,15 +33,19 @@ delays v1.10 4/21/97 Fixed module code so that multiple cards may be detected, other cleanups. -djb + v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb + v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb + v1.15 1/31/98 Faster recovery for Tx errors. -djb + v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb */ -static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n"; +static char *version = "3c509.c:1.16 2/3/98 becker@cesdis.gsfc.nasa.gov\n"; /* A few values that may be tweaked. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (400*HZ/1000) /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -#define INTR_WORK 10 +static int max_interrupt_work = 10; #include @@ -128,11 +132,11 @@ int head, size; struct sk_buff *queue[SKB_QUEUE_SIZE]; }; -static int id_port = 0x100; +static int id_port = 0x110; /* Start with 0x110 to avoid new sound cards.*/ static struct device *el3_root_dev = NULL; static ushort id_read_eeprom(int index); -static ushort read_eeprom(short ioaddr, int index); +static ushort read_eeprom(int ioaddr, int index); static int el3_open(struct device *dev); static int el3_start_xmit(struct sk_buff *skb, struct device *dev); static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); @@ -147,8 +151,8 @@ int el3_probe(struct device *dev) { short lrs_state = 0xff, i; - ushort ioaddr, irq, if_port; - short phys_addr[3]; + int ioaddr, irq, if_port; + u16 phys_addr[3]; static int current_tag = 0; /* First check all slots of the EISA bus. The next slot address to @@ -202,7 +206,7 @@ outb(0x02, 0x279); /* Select PnP config control register. */ outb(0x02, 0xA79); /* Return to WaitForKey state. */ /* Select an open I/O location at 0x1*0 to do contention select. */ - for (id_port = 0x100; id_port < 0x200; id_port += 0x10) { + for ( ; id_port < 0x200; id_port += 0x10) { if (check_region(id_port, 1)) continue; outb(0x00, id_port); @@ -246,18 +250,23 @@ } { - unsigned short iobase = id_read_eeprom(8); + unsigned int iobase = id_read_eeprom(8); if_port = iobase >> 14; ioaddr = 0x200 + ((iobase & 0x1f) << 4); } - if (dev && dev->irq > 1 && dev->irq < 16) - irq = dev->irq; - else - irq = id_read_eeprom(9) >> 12; + irq = id_read_eeprom(9) >> 12; - if (dev && dev->base_addr != 0 - && dev->base_addr != (unsigned short)ioaddr) { - return -ENODEV; + if (dev) { /* Set passed-in IRQ or I/O Addr. */ + if (dev->irq > 1 && dev->irq < 16) + irq = dev->irq; + + if (dev->base_addr) { + if (dev->mem_end == 0x3c509 /* Magic key */ + && dev->base_addr >= 0x200 && dev->base_addr <= 0x3e0) + ioaddr = dev->base_addr & 0x3f0; + else if (dev->base_addr != ioaddr) + return -ENODEV; + } } /* Set the adaptor tag so that the next card can be found. */ @@ -322,7 +331,7 @@ /* Read a word from the EEPROM using the regular EEPROM access register. Assume that we are in register window zero. */ -static ushort read_eeprom(short ioaddr, int index) +static ushort read_eeprom(int ioaddr, int index) { outw(EEPROM_READ + index, ioaddr + 10); /* Pause for at least 162 us. for the read to take place. */ @@ -419,7 +428,7 @@ /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull, + outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, ioaddr + EL3_CMD); if (el3_debug > 3) @@ -521,7 +530,7 @@ { struct device *dev = (struct device *)dev_id; int ioaddr, status; - int i = INTR_WORK; + int i = max_interrupt_work; if (dev == NULL) { printk ("el3_interrupt(): irq %d for unknown device.\n", irq); @@ -552,7 +561,7 @@ dev->tbusy = 0; mark_bh(NET_BH); } - if (status & (AdapterFailure | RxEarly | StatsFull)) { + if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { /* Handle all uncommon interrupts. */ if (status & StatsFull) /* Empty statistics. */ update_stats(ioaddr, dev); @@ -560,6 +569,18 @@ el3_rx(dev); outw(AckIntr | RxEarly, ioaddr + EL3_CMD); } + if (status & TxComplete) { /* Really Tx error. */ + struct el3_private *lp = (struct el3_private *)dev->priv; + short tx_status; + int i = 4; + + while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { + if (tx_status & 0x38) lp->stats.tx_aborted_errors++; + if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); + if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); + outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ + } + } if (status & AdapterFailure) { /* Adapter failure requires Rx reset and reinit. */ outw(RxReset, ioaddr + EL3_CMD); @@ -653,6 +674,8 @@ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ short error = rx_status & 0x3800; + + outw(RxDiscard, ioaddr + EL3_CMD); lp->stats.rx_errors++; switch (error) { case 0x0000: lp->stats.rx_over_errors++; break; @@ -683,19 +706,21 @@ (pkt_len + 3) >> 2); #endif + outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ lp->stats.rx_packets++; continue; - } else if (el3_debug) - printk(KERN_WARNING "%s: Couldn't allocate a sk_buff of size %d.\n", + } + outw(RxDiscard, ioaddr + EL3_CMD); + lp->stats.rx_dropped++; + if (el3_debug) + printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } - lp->stats.rx_dropped++; - outw(RxDiscard, ioaddr + EL3_CMD); + inw(ioaddr + EL3_STATUS); /* Delay. */ while (inw(ioaddr + EL3_STATUS) & 0x1000) - printk(KERN_DEBUG " Waiting for 3c509 to discard packet, status %x.\n", + printk(" Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); } @@ -708,7 +733,7 @@ static void set_multicast_list(struct device *dev) { - short ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; if (el3_debug > 1) { static int old = 0; if (old != dev->mc_count) { @@ -766,7 +791,7 @@ } #ifdef MODULE -/* Parameter that may be passed into the module. */ +/* Parameters that may be passed into the module. */ static int debug = -1; static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1}; diff -u --recursive --new-file v2.0.35/linux/drivers/net/3c515.c linux/drivers/net/3c515.c --- v2.0.35/linux/drivers/net/3c515.c Sun Nov 15 10:49:35 1998 +++ linux/drivers/net/3c515.c Sun Nov 15 10:33:01 1998 @@ -39,7 +39,6 @@ #define RX_RING_SIZE 16 #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ -#include #ifdef MODULE #ifdef MODVERSIONS #include diff -u --recursive --new-file v2.0.35/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c --- v2.0.35/linux/drivers/net/3c59x.c Sun Nov 15 10:49:36 1998 +++ linux/drivers/net/3c59x.c Sun Nov 15 10:33:02 1998 @@ -20,9 +20,9 @@ /* "Knobs" that adjust features and parameters. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1512 effectively disables this feature. */ -static const int rx_copybreak = 200; +static const rx_copybreak = 200; /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ -static const int mtu = 1500; +static const mtu = 1500; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; @@ -44,9 +44,7 @@ programmed-I/O for Vortex cards. Full-bus-master transfers are always enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, the feature may be turned on using 'options'. */ -#if YOU_ARE_BRAVER_THAN_ME #define VORTEX_BUS_MASTER -#endif /* A few values that may be tweaked. */ /* Time in jiffies before concluding the transmitter is hung. */ @@ -1761,7 +1759,8 @@ printk(KERN_DEBUG " In boomerang_rx(), status %4.4x, rx_status " "%4.4x.\n", inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); - while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { + while ((--rx_work_limit >= 0) && + ((rx_status = vp->rx_ring[entry].status) & RxDComplete)) { if (rx_status & RxDError) { /* Error, update stats. */ unsigned char rx_error = rx_status >> 16; if (vortex_debug > 2) @@ -1801,6 +1800,10 @@ void *temp; /* Pass up the skbuff already on the Rx ring. */ skb = vp->rx_skbuff[entry]; + if (skb == NULL) { + printk(KERN_WARNING "%s: in boomerang_rx -- attempt to use NULL skb caught\n", dev->name); + break; + } vp->rx_skbuff[entry] = NULL; #if LINUX_VERSION_CODE >= 0x10300 temp = skb_put(skb, pkt_len); @@ -1833,8 +1836,6 @@ vp->stats.rx_packets++; } entry = (++vp->cur_rx) % RX_RING_SIZE; - if (--rx_work_limit < 0) - break; } /* Refill the Rx ring buffers. */ for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) { @@ -1842,8 +1843,10 @@ entry = vp->dirty_rx % RX_RING_SIZE; if (vp->rx_skbuff[entry] == NULL) { skb = DEV_ALLOC_SKB(PKT_BUF_SZ); - if (skb == NULL) + if (skb == NULL) { + printk(KERN_DEBUG "%s: in boomerang_rx -- could not allocate skbuff\n", dev->name); break; /* Bad news! */ + } skb->dev = dev; /* Mark as being used by this device. */ #if LINUX_VERSION_CODE > 0x10300 skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ @@ -1856,6 +1859,12 @@ vp->rx_ring[entry].status = 0; /* Clear complete bit. */ outw(UpUnstall, ioaddr + EL3_CMD); } + + if (vp->dirty_rx >= RX_RING_SIZE ) { + vp->cur_rx -= RX_RING_SIZE; + vp->dirty_rx -= RX_RING_SIZE; + } + return 0; } diff -u --recursive --new-file v2.0.35/linux/drivers/net/Changelog.tlan linux/drivers/net/Changelog.tlan --- v2.0.35/linux/drivers/net/Changelog.tlan Sun Nov 15 10:49:37 1998 +++ linux/drivers/net/Changelog.tlan Sun Nov 15 10:33:02 1998 @@ -1,5 +1,8 @@ TLan Device Driver change log. +1.0 - Stopped ignoring devices that didn't has the bus master + flag set. + 0.43 - Changed the id strings of the Compaq devices to their real names. - Added 2 other Olicom devices with a patch provided by @@ -66,7 +69,7 @@ 100Mbs should work now on 0xAE32. - Fixed a small bug where heartbeat and PHY interrupts were always being enabled. - - Force the the driver into Unmanaged PHY mode for 0xF130 devices, + - Force the driver into Unmanaged PHY mode for 0xF130 devices, even if a managed (ie, the built-in one) PHY is detected. - Moved the PHY initialization to after the onboard PHY is enabled, if selected. diff -u --recursive --new-file v2.0.35/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.0.35/linux/drivers/net/Config.in Sun Nov 15 10:49:37 1998 +++ linux/drivers/net/Config.in Sun Nov 15 10:33:02 1998 @@ -46,7 +46,6 @@ tristate 'Z8530 SCC KISS emulation driver for AX.25' CONFIG_SCC tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN - tristate 'WIC Radio IP bridge (EXPERIMENTAL)' CONFIG_WIC fi # # Ethernet @@ -83,9 +82,8 @@ tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 tristate 'SMC EPIC/100 (EtherPower II) support' CONFIG_EPIC - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN - fi + tristate 'TI ThunderLAN support' CONFIG_TLAN + tristate 'VIA Rhine support' CONFIG_VIA_RHINE fi bool 'Other ISA cards' CONFIG_NET_ISA if [ "$CONFIG_NET_ISA" = "y" ]; then @@ -147,3 +145,7 @@ bool ' Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051 fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER + tristate 'Red Creek Hardware VPN (EXPERIMENTAL)' CONFIG_RCPCI +fi diff -u --recursive --new-file v2.0.35/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.0.35/linux/drivers/net/Makefile Sun Nov 15 10:49:37 1998 +++ linux/drivers/net/Makefile Sun Nov 15 10:33:02 1998 @@ -3,7 +3,7 @@ # Makefile for the Linux network (ethercard) device drivers. # -# This will go away in some future future: hidden configuration files +# This will go away in some future: hidden configuration files # are difficult for users to deal with. include CONFIG @@ -53,6 +53,14 @@ endif endif +ifeq ($(CONFIG_SHAPER),y) +L_OBJS += shaper.o +else + ifeq ($(CONFIG_SHAPER),m) + M_OBJS += shaper.o + endif +endif + ifeq ($(CONFIG_SK_G16),y) L_OBJS += sk_g16.o endif @@ -73,14 +81,6 @@ endif endif -ifeq ($(CONFIG_WIC),y) -L_OBJS += wic.o -else - ifeq ($(CONFIG_WIC),m) - M_OBJS += wic.o - endif -endif - ifeq ($(CONFIG_SMC9194),y) L_OBJS += smc9194.o else @@ -319,6 +319,14 @@ endif endif +ifeq ($(CONFIG_VIA_RHINE),y) +L_OBJS += via-rhine.o +else + ifeq ($(CONFIG_VIA_RHINE),m) + M_OBJS += via-rhine.o + endif +endif + ifeq ($(CONFIG_EEXPRESS),y) L_OBJS += eexpress.o else @@ -659,10 +667,23 @@ endif endif + + +ifeq ($(CONFIG_RCPCI),y) +L_OBJS += rcpci.o +else + ifeq ($(CONFIG_RCPCI),m) + M_OBJS += rcpci.o + endif +endif + include $(TOPDIR)/Rules.make clean: rm -f core *.o *.a *.s + +rcpci.o: rcpci45.o rcmtl.o + $(LD) -r -o rcpci.o rcpci45.o rcmtl.o wd.o: wd.c CONFIG $(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $< diff -u --recursive --new-file v2.0.35/linux/drivers/net/README.eql linux/drivers/net/README.eql --- v2.0.35/linux/drivers/net/README.eql Mon May 6 02:26:07 1996 +++ linux/drivers/net/README.eql Sun Nov 15 10:33:02 1998 @@ -257,9 +257,9 @@ 5. Tester's Reports Some people have experimented with the eql device with newer kernels - kernels (than 1.1.75). I have since updated the driver to patch - cleanly in newer kernels because of the removal of the old "slave- - balancing" driver config option. + (than 1.1.75). I have since updated the driver to patch cleanly in + newer kernels because of the removal of the old "slave-balancing" + driver config option. o icee from LinuxNET patched 1.1.86 without any rejects and was able diff -u --recursive --new-file v2.0.35/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.0.35/linux/drivers/net/Space.c Sun Nov 15 10:49:37 1998 +++ linux/drivers/net/Space.c Sun Nov 15 10:33:02 1998 @@ -61,6 +61,7 @@ extern int ewrk3_probe(struct device *); extern int de4x5_probe(struct device *); extern int el1_probe(struct device *); +extern int via_rhine_probe(struct device *); #if defined(CONFIG_WAVELAN) extern int wavelan_probe(struct device *); #endif /* defined(CONFIG_WAVELAN) */ @@ -92,6 +93,10 @@ extern int atp_init(struct device *); extern int de600_probe(struct device *); extern int de620_probe(struct device *); +/* The shaper hook */ +extern int shaper_probe(struct device *); +/* Red Creek PCI hook */ +extern int rcpci_probe(struct device *); static int ethif_probe(struct device *dev) @@ -127,6 +132,9 @@ #ifdef CONFIG_RTL8139 && rtl8139_probe(dev) #endif +#if defined(CONFIG_VIA_RHINE) + && via_rhine_probe(dev) +#endif #if defined(CONFIG_VORTEX) && tc59x_probe(dev) #endif @@ -359,6 +367,20 @@ #undef NEXT_DEV #define NEXT_DEV (&strip_bootstrap) #endif /* STRIP */ + +#if defined(CONFIG_SHAPER) +static struct device shaper_bootstrap = { + "shaper", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, shaper_probe, }; +#undef NEXT_DEV +#define NEXT_DEV (&shaper_bootstrap) +#endif /* SHAPER */ + +#if defined(CONFIG_RCPCI) +static struct device rcpci_bootstrap = { + "rcpci", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, rcpci_probe, }; +#undef NEXT_DEV +#define NEXT_DEV (&rcpci_bootstrap) +#endif /* RCPCI */ #if defined(CONFIG_PPP) extern int ppp_init(struct device *); diff -u --recursive --new-file v2.0.35/linux/drivers/net/apricot.c linux/drivers/net/apricot.c --- v2.0.35/linux/drivers/net/apricot.c Thu Feb 29 21:50:42 1996 +++ linux/drivers/net/apricot.c Sun Nov 15 10:33:02 1998 @@ -147,14 +147,14 @@ }; struct i596_private { - struct i596_scp scp; - struct i596_iscp iscp; - struct i596_scb scb; - struct i596_cmd set_add; + volatile struct i596_scp scp; + volatile struct i596_iscp iscp; + volatile struct i596_scb scb; + volatile struct i596_cmd set_add; char eth_addr[8]; - struct i596_cmd set_conf; + volatile struct i596_cmd set_conf; char i596_config[16]; - struct i596_cmd tdr; + volatile struct i596_cmd tdr; unsigned long stat; int last_restart; struct i596_rfd *rx_tail; @@ -930,7 +930,7 @@ while (lp->scb.status, lp->scb.command) if (--boguscnt == 0) { - printk("%s: close timed timed out with status %4.4x, cmd %4.4x.\n", + printk("%s: close timed out with status %4.4x, cmd %4.4x.\n", dev->name, lp->scb.status, lp->scb.command); break; } diff -u --recursive --new-file v2.0.35/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c --- v2.0.35/linux/drivers/net/de4x5.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/de4x5.c Sun Nov 15 10:33:02 1998 @@ -372,11 +372,43 @@ 0.534 24-Jan-98 Fix last (?) endian bug from 0.535 21-Feb-98 Fix Ethernet Address PROM reset bug for DC21040. + 0.5351 4-Oct-98 Atomicize assertion of dev->interrupt for SMP (not + for Alpha arch.) from + Add TP, AUI and BNC cases to 21140m_autoconf() for + case where a 21140 under SROM control uses, e.g. AUI + from problem report by + Add MII parallel detection to 2114x_autoconf() for + case where no autonegotiation partner exists from + problem report by . + Add ability to force connection type directly even + when using SROM control from problem report by + . + Fix is_anc_capable() bug reported by + . + Fix type[13]_infoblock() bug: during MII search, PHY + lp->rst not run because lp->ibn not initialised - + from report & fix by . + Fix probe bug with EISA & PCI cards present from + report by . + Fix compiler problems associated with i386-string + ops from multiple bug reports and temporary fix + from . + Add an_exception() for old ZYNX346 and fix compile + warning on PPC & SPARC, from . + Fix lastPCI to correctly work with compiled in + kernels and modules from bug report by + et al. + Fix dc2114x_autoconf() to stop multiple messages + when media is unconnected. + Change dev->interrupt to lp->interrupt to ensure + alignment for Alpha's and avoid their unaligned + access traps. This flag is merely for log messages: + should do something more definitive though... ========================================================================= */ -static const char *version = "de4x5.c:V0.535 1998/2/21 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:V0.5351 1998/10/4 davies@maniac.ultranet.com\n"; #include @@ -728,6 +760,7 @@ struct de4x5_private { char adapter_name[80]; /* Adapter name */ + u_long interrupt; /* Aligned ISR flag */ struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring */ struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring */ struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */ @@ -948,7 +981,8 @@ static int get_hw_addr(struct device *dev); static void srom_repair(struct device *dev, int card); static int test_bad_enet(struct device *dev, int status); -#ifndef __sparc_v9__ +static int an_exception(struct bus_type *lp); +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) static void eisa_probe(struct device *dev, u_long iobase); #endif static void pci_probe(struct device *dev, u_long iobase); @@ -999,12 +1033,15 @@ #endif /* MODULE */ static char name[DE4X5_NAME_LENGTH + 1]; -#ifndef __sparc_v9__ +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; +static int lastEISA = 0; +#else +static int lastEISA = MAX_EISA_SLOTS; /* Only PCI probes */ #endif static int num_de4x5s = 0; static int cfrv = 0, useSROM = 0; -static int lastEISA = 0, lastPCI = -1; +static int lastPCI = -1; static struct device *lastModule = NULL; /* @@ -1069,10 +1106,12 @@ { u_long iobase = dev->base_addr; -#ifndef __sparc_v9__ +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) eisa_probe(dev, iobase); #endif - pci_probe(dev, iobase); + if (lastEISA == MAX_EISA_SLOTS) { + pci_probe(dev, iobase); + } return (dev->priv ? 0 : -ENODEV); } @@ -1169,21 +1208,15 @@ /* ** Choose correct autosensing in case someone messed up */ - if ((lp->params.autosense & AUTO) || lp->useSROM) { - lp->autosense = AUTO; - } else { - if (lp->chipset != DC21140) { - if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { - lp->params.autosense = TP; - } - if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { - lp->params.autosense = BNC; - } - lp->autosense = lp->params.autosense & 0x001f; - } else { - lp->autosense = lp->params.autosense & 0x00c0; - } - } + lp->autosense = lp->params.autosense; + if (lp->chipset != DC21140) { + if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { + lp->params.autosense = TP; + } + if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { + lp->params.autosense = BNC; + } + } lp->fdx = lp->params.fdx; sprintf(lp->adapter_name,"%s (%s)", name, dev->name); @@ -1347,7 +1380,7 @@ dev->tbusy = 0; dev->start = 1; - dev->interrupt = UNMASK_INTERRUPTS; + lp->interrupt = UNMASK_INTERRUPTS; dev->trans_start = jiffies; START_DE4X5; @@ -1467,7 +1500,7 @@ } /* -** Writes a socket buffer address to the next available transmit descriptor +** Writes a socket buffer address to the next available transmit descriptor. */ static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev) @@ -1491,12 +1524,12 @@ sti(); /* Test if cache is already locked - requeue skb if so */ - if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) + if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) return -1; /* Transmit descriptor ring full or stale skb */ if (dev->tbusy || lp->tx_skb[lp->tx_new]) { - if (dev->interrupt) { + if (lp->interrupt) { de4x5_putb_cache(dev, skb); /* Requeue the buffer */ } else { de4x5_put_cache(dev, skb); @@ -1506,7 +1539,7 @@ } } else if (skb->len > 0) { /* If we already have stuff queued locally, use that first */ - if (lp->cache.skb && !dev->interrupt) { + if (lp->cache.skb && !lp->interrupt) { de4x5_put_cache(dev, skb); skb = de4x5_get_cache(dev); } @@ -1563,14 +1596,14 @@ lp = (struct de4x5_private *)dev->priv; iobase = dev->base_addr; - if (dev->interrupt) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - DISABLE_IRQs; /* Ensure non re-entrancy */ + + if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt)) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + #if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) synchronize_irq(); #endif - dev->interrupt = MASK_INTERRUPTS; for (limit=0; limit<8; limit++) { sts = inl(DE4X5_STS); /* Read IRQ status */ @@ -1608,7 +1641,7 @@ lp->cache.lock = 0; } - dev->interrupt = UNMASK_INTERRUPTS; + lp->interrupt = UNMASK_INTERRUPTS; ENABLE_IRQs; return; @@ -1740,7 +1773,7 @@ if (TX_BUFFS_AVAIL && dev->tbusy) { /* Any resources available? */ dev->tbusy = 0; /* Clear TX busy flag */ - if (dev->interrupt) mark_bh(NET_BH); + if (lp->interrupt) mark_bh(NET_BH); } return 0; @@ -2001,7 +2034,7 @@ return; } -#ifndef __sparc_v9__ +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) /* ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually ** the motherboard. Upto 15 EISA devices are supported. @@ -2069,7 +2102,7 @@ return; } -#endif /* !(__sparc_v9__) */ +#endif /* !(__sparc_v9__) && !(__powerpc__) && !defined(__alpha__)*/ /* ** PCI bus I/O device probe @@ -2197,10 +2230,8 @@ dev->irq = irq; if ((status = de4x5_hw_init(dev, iobase)) == 0) { num_de4x5s++; - if (loading_module) { - link_modules(lastModule, dev); - lastPCI = index; - } + lastPCI = index; + if (loading_module) link_modules(lastModule, dev); return; } } else if (ioaddr != 0) { @@ -2210,7 +2241,7 @@ } } - if (loading_module) lastPCI = NO_MORE_PCI; + lastPCI = NO_MORE_PCI; return; } @@ -2381,7 +2412,7 @@ s32 imr; switch (lp->media) { - case INIT: + case INIT: DISABLE_IRQs; lp->tx_enable = NO; lp->timeout = -1; @@ -2399,36 +2430,36 @@ next_tick = dc21040_autoconf(dev); break; - case TP: + case TP: next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, TP_SUSPECT, test_tp); break; - case TP_SUSPECT: + case TP_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf); break; - case BNC: - case AUI: - case BNC_AUI: + case BNC: + case AUI: + case BNC_AUI: next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, BNC_AUI_SUSPECT, ping_media); break; - case BNC_AUI_SUSPECT: + case BNC_AUI_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf); break; - case EXT_SIA: + case EXT_SIA: next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, NC, EXT_SIA_SUSPECT, ping_media); break; - case EXT_SIA_SUSPECT: + case EXT_SIA_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf); break; - case NC: + case NC: /* default to TP for all */ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000); if (lp->media != lp->c_media) { @@ -2453,13 +2484,13 @@ int linkBad; switch (lp->local_state) { - case 0: + case 0: reset_init_sia(dev, csr13, csr14, csr15); lp->local_state++; next_tick = 500; break; - case 1: + case 1: if (!lp->tx_enable) { linkBad = fn(dev, timeout); if (linkBad < 0) { @@ -2492,7 +2523,7 @@ int linkBad; switch (lp->local_state) { - case 1: + case 1: if (lp->linkOK) { lp->media = prev_state; } else { @@ -2501,7 +2532,7 @@ } break; - case 2: + case 2: linkBad = fn(dev, timeout); if (linkBad < 0) { next_tick = linkBad & ~TIMER_CB; @@ -2535,7 +2566,7 @@ int next_tick = DE4X5_AUTOSENSE_MS; switch (lp->media) { - case INIT: + case INIT: DISABLE_IRQs; lp->tx_enable = NO; lp->timeout = -1; @@ -2555,7 +2586,7 @@ next_tick = dc21041_autoconf(dev); break; - case TP_NW: + case TP_NW: if (lp->timeout < 0) { omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ outl(omr | OMR_FDX, DE4X5_OMR); @@ -2575,7 +2606,7 @@ } break; - case ANS: + case ANS: if (!lp->tx_enable) { irqs = STS_LNP; irq_mask = IMR_LPM; @@ -2597,11 +2628,11 @@ } break; - case ANS_SUSPECT: + case ANS_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf); break; - case TP: + case TP: if (!lp->tx_enable) { if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ @@ -2631,11 +2662,11 @@ } break; - case TP_SUSPECT: + case TP_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf); break; - case AUI: + case AUI: if (!lp->tx_enable) { if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ @@ -2661,13 +2692,13 @@ } break; - case AUI_SUSPECT: + case AUI_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf); break; - case BNC: + case BNC: switch (lp->local_state) { - case 0: + case 0: if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ outl(omr & ~OMR_FDX, DE4X5_OMR); @@ -2683,7 +2714,7 @@ } break; - case 1: + case 1: if (!lp->tx_enable) { if ((sts = ping_media(dev, 3000)) < 0) { next_tick = sts & ~TIMER_CB; @@ -2703,11 +2734,11 @@ } break; - case BNC_SUSPECT: + case BNC_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf); break; - case NC: + case NC: omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ outl(omr | OMR_FDX, DE4X5_OMR); reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ @@ -2737,7 +2768,7 @@ u_long imr, omr, iobase = dev->base_addr; switch(lp->media) { - case INIT: + case INIT: if (lp->timeout < 0) { DISABLE_IRQs; lp->tx_enable = FALSE; @@ -2783,9 +2814,9 @@ } break; - case ANS: + case ANS: switch (lp->local_state) { - case 0: + case 0: if (lp->timeout < 0) { mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); } @@ -2803,7 +2834,7 @@ } break; - case 1: + case 1: if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { next_tick = sr & ~TIMER_CB; } else { @@ -2831,7 +2862,7 @@ } break; - case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ if (lp->timeout < 0) { lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : (~gep_rd(dev) & GEP_LNP)); @@ -2851,7 +2882,7 @@ } break; - case _100Mb: /* Set 100Mb/s */ + case _100Mb: /* Set 100Mb/s */ next_tick = 3000; if (!lp->tx_enable) { SET_100Mb; @@ -2867,7 +2898,9 @@ } break; - case _10Mb: /* Set 10Mb/s */ + case BNC: + case AUI: + case _10Mb: /* Set 10Mb/s */ next_tick = 3000; if (!lp->tx_enable) { SET_10Mb; @@ -2883,7 +2916,7 @@ } break; - case NC: + case NC: if (lp->media != lp->c_media) { de4x5_dbg_media(dev); lp->c_media = lp->media; @@ -2919,33 +2952,54 @@ int next_tick = DE4X5_AUTOSENSE_MS; switch (lp->media) { - case INIT: + case INIT: if (lp->timeout < 0) { DISABLE_IRQs; lp->tx_enable = FALSE; lp->linkOK = 0; lp->timeout = -1; - de4x5_save_skbs(dev); /* Save non transmitted skb's */ + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + if (lp->params.autosense & ~AUTO) { + srom_map_media(dev); /* Fixed media requested */ + if (lp->media != lp->params.autosense) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + lp->media = INIT; + } } if ((next_tick = de4x5_reset_phy(dev)) < 0) { next_tick &= ~TIMER_CB; } else { - lp->media = SPD_DET; - if ((lp->infoblock_media == ANS) && + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if (lp->autosense == TP) { + lp->media = TP; + } else if (lp->autosense == BNC) { + lp->media = BNC; + } else if (lp->autosense == AUI) { + lp->media = AUI; + } else { + lp->media = SPD_DET; + if ((lp->infoblock_media == ANS) && ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); lp->media = ANS; + } } lp->local_state = 0; next_tick = dc2114x_autoconf(dev); } break; - case ANS: + case ANS: switch (lp->local_state) { - case 0: + case 0: if (lp->timeout < 0) { mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); } @@ -2963,7 +3017,7 @@ } break; - case 1: + case 1: if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { next_tick = sr & ~TIMER_CB; } else { @@ -2985,15 +3039,15 @@ } } /* Auto Negotiation failed to finish */ next_tick = dc2114x_autoconf(dev); - } /* Auto Negotiation failed to start */ + } /* Auto Negotiation failed to start */ break; } break; - case AUI: + case AUI: if (!lp->tx_enable) { if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ outl(omr & ~OMR_FDX, DE4X5_OMR); } irqs = 0; @@ -3016,15 +3070,15 @@ } break; - case AUI_SUSPECT: + case AUI_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); break; - case BNC: + case BNC: switch (lp->local_state) { - case 0: + case 0: if (lp->timeout < 0) { - omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ outl(omr & ~OMR_FDX, DE4X5_OMR); } irqs = 0; @@ -3033,12 +3087,12 @@ if (sts < 0) { next_tick = sts & ~TIMER_CB; } else { - lp->local_state++; /* Ensure media connected */ + lp->local_state++; /* Ensure media connected */ next_tick = dc2114x_autoconf(dev); } break; - case 1: + case 1: if (!lp->tx_enable) { if ((sts = ping_media(dev, 3000)) < 0) { next_tick = sts & ~TIMER_CB; @@ -3059,11 +3113,11 @@ } break; - case BNC_SUSPECT: + case BNC_SUSPECT: next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); break; - case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ if (srom_map_media(dev) < 0) { lp->tcount++; lp->media = INIT; @@ -3080,9 +3134,17 @@ return PDET_LINK_WAIT; } } - if (((lp->media == _100Mb) && is_100_up(dev)) || - ((lp->media == _10Mb) && is_10_up(dev)) || - (lp->media == BNC) || (lp->media == AUI)) { + if (lp->media == ANS) { /* Do MII parallel detection */ + if (is_spd_100(dev)) { + lp->media = _100Mb; + } else { + lp->media = _10Mb; + } + next_tick = dc2114x_autoconf(dev); + } else if (((lp->media == _100Mb) && is_100_up(dev)) || + (((lp->media == _10Mb) || (lp->media == TP) || + (lp->media == BNC) || (lp->media == AUI)) && + is_10_up(dev))) { next_tick = dc2114x_autoconf(dev); } else { lp->tcount++; @@ -3090,7 +3152,7 @@ } break; - case _10Mb: + case _10Mb: next_tick = 3000; if (!lp->tx_enable) { SET_10Mb; @@ -3106,7 +3168,7 @@ } break; - case _100Mb: + case _100Mb: next_tick = 3000; if (!lp->tx_enable) { SET_100Mb; @@ -3122,7 +3184,7 @@ } break; - default: + default: lp->tcount++; printk("Huh?: media:%02x\n", lp->media); lp->media = INIT; @@ -3492,7 +3554,7 @@ if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); } else if ((lp->chipset & ~0x00ff) == DC2114x) { - return (inl(DE4X5_SISR) & SISR_LPN) >> 11; + return (inl(DE4X5_SISR) & SISR_LPN) >> 12; } else { return 0; } @@ -3905,7 +3967,7 @@ static int EISA_signature(char *name, s32 eisa_id) { - c_char *signatures[] = DE4X5_SIGNATURE; + static c_char *signatures[] = DE4X5_SIGNATURE; char ManCode[DE4X5_STRLEN]; union { s32 ID; @@ -3940,7 +4002,7 @@ static int PCI_signature(char *name, struct bus_type *lp) { - c_char *de4x5_signatures[] = DE4X5_SIGNATURE; + static c_char *de4x5_signatures[] = DE4X5_SIGNATURE; int i, status = 0, siglen = sizeof(de4x5_signatures)/sizeof(c_char *); if (lp->chipset == DC21040) { @@ -4119,6 +4181,21 @@ /* If possible, try to fix a broken card - SMC only so far */ srom_repair(dev, broken); +#ifdef CONFIG_PMAC + /* + ** If the address starts with 00 a0, we have to bit-reverse + ** each byte of the address. + */ + if (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0xa0) { + for (i = 0; i < ETH_ALEN; ++i) { + int x = dev->dev_addr[i]; + x = ((x & 0xf) << 4) + ((x & 0xf0) >> 4); + x = ((x & 0x33) << 2) + ((x & 0xcc) >> 2); + dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); + } + } +#endif /* CONFIG_PMAC */ + /* Test for a bad enet address */ status = test_bad_enet(dev, status); @@ -4198,7 +4275,9 @@ if (dev->dev_addr[i] != 0) break; } for (i=0; idev_addr[i]; - dev->irq = last.irq; + if (!an_exception(lp)) { + dev->irq = last.irq; + } status = 0; } @@ -4213,6 +4292,20 @@ } /* +** List of board exceptions with correctly wired IRQs +*/ +static int +an_exception(struct bus_type *lp) +{ + if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) && + (*(u_short *)lp->srom.sub_system_id == 0x95e0)) { + return -1; + } + + return 0; +} + +/* ** SROM Read */ static short @@ -4658,6 +4751,7 @@ p += 2; if (lp->state == INITIALISED) { + lp->ibn = 1; lp->active = *p++; lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); @@ -4737,6 +4831,7 @@ p += 2; if (lp->state == INITIALISED) { + lp->ibn = 3; lp->active = *p++; lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1); lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1); @@ -5484,24 +5579,24 @@ } tmp; switch(ioc->cmd) { - case DE4X5_GET_HWADDR: /* Get the hardware address */ + case DE4X5_GET_HWADDR: /* Get the hardware address */ ioc->len = ETH_ALEN; status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len); if (status) - break; + break; for (i=0; idev_addr[i]; } copy_to_user(ioc->data, tmp.addr, ioc->len); break; - case DE4X5_SET_HWADDR: /* Set the hardware address */ + case DE4X5_SET_HWADDR: /* Set the hardware address */ status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN); if (status) - break; + break; status = -EPERM; if (!suser()) - break; + break; status = 0; copy_from_user(tmp.addr, ioc->data, ETH_ALEN); for (i=0; itbusy = 0; /* Unlock the TX ring */ break; - case DE4X5_SET_PROM: /* Set Promiscuous Mode */ + case DE4X5_SET_PROM: /* Set Promiscuous Mode */ if (suser()) { omr = inl(DE4X5_OMR); omr |= OMR_PR; @@ -5528,7 +5623,7 @@ } break; - case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */ + case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */ if (suser()) { omr = inl(DE4X5_OMR); omr &= ~OMR_PR; @@ -5539,11 +5634,11 @@ } break; - case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */ + case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */ printk("%s: Boo!\n", dev->name); break; - case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ + case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ if (suser()) { omr = inl(DE4X5_OMR); omr |= OMR_PM; @@ -5553,18 +5648,18 @@ } break; - case DE4X5_GET_STATS: /* Get the driver statistics */ + case DE4X5_GET_STATS: /* Get the driver statistics */ ioc->len = sizeof(lp->pktStats); status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len); if (status) - break; + break; cli(); copy_to_user(ioc->data, &lp->pktStats, ioc->len); sti(); break; - case DE4X5_CLR_STATS: /* Zero out the driver statistics */ + case DE4X5_CLR_STATS: /* Zero out the driver statistics */ if (suser()) { cli(); memset(&lp->pktStats, 0, sizeof(lp->pktStats)); @@ -5574,14 +5669,14 @@ } break; - case DE4X5_GET_OMR: /* Get the OMR Register contents */ + case DE4X5_GET_OMR: /* Get the OMR Register contents */ tmp.addr[0] = inl(DE4X5_OMR); if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) { copy_to_user(ioc->data, tmp.addr, 1); } break; - case DE4X5_SET_OMR: /* Set the OMR Register contents */ + case DE4X5_SET_OMR: /* Set the OMR Register contents */ if (suser()) { if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) { copy_from_user(tmp.addr, ioc->data, 1); @@ -5592,7 +5687,7 @@ } break; - case DE4X5_GET_REG: /* Get the DE4X5 Registers */ + case DE4X5_GET_REG: /* Get the DE4X5 Registers */ j = 0; tmp.lval[0] = inl(DE4X5_STS); j+=4; tmp.lval[1] = inl(DE4X5_BMR); j+=4; @@ -5700,7 +5795,7 @@ break; */ - default: + default: status = -EOPNOTSUPP; } @@ -5781,16 +5876,17 @@ static int count_adapters(void) { - int i, j; - char name[DE4X5_STRLEN]; + int i, j=0; u_char pb, dev_fn, dev_num; u_short dev_id, vendor; u_int class = DE4X5_CLASS_CODE; u_int device; -#ifndef __sparc_v9__ + +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) + char name[DE4X5_STRLEN]; u_long iobase = 0x1000; - for (j=0, i=1; i @@ -113,22 +119,26 @@ #include #include - /* First, a few definitions that the brave might change. */ + /* A zero-terminated list of I/O addresses to be probed. */ static unsigned int eepro_portlist[] = - { 0x200, 0x240, 0x260, 0x280, 0x2C0, 0x300, 0x310, 0x320, 0x340, 0x360, 0}; + { 0x300, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0}; /* use 0 for production, 1 for verification, >2 for debug */ + #ifndef NET_DEBUG -#define NET_DEBUG 3 +#define NET_DEBUG 1 #endif + static unsigned int net_debug = NET_DEBUG; /* The number of low I/O ports used by the ethercard. */ + #define EEPRO_IO_EXTENT 16 /* Different 82595 chips */ + #define LAN595 0 #define LAN595TX 1 #define LAN595FX 2 @@ -140,18 +150,29 @@ unsigned tx_start; /* start of the transmit chain */ int tx_last; /* pointer to last packet in the transmit chain */ unsigned tx_end; /* end of the transmit chain (plus 1) */ - int eepro; /* a flag, TRUE=1 for the EtherExpress Pro/10, - FALSE = 0 for other 82595-based lan cards. */ + int eepro; /* 1 for the EtherExpress Pro/10, + 2 for the EtherExpress Pro/10+, + 0 for other 82595-based lan cards. */ int version; /* a flag to indicate if this is a TX or FX version of the 82595 chip. */ int stepping; }; /* The station (ethernet) address prefix, used for IDing the board. */ -#define SA_ADDR0 0x00 + +#define SA_ADDR0 0x00 /* Etherexpress Pro/10 */ #define SA_ADDR1 0xaa #define SA_ADDR2 0x00 +#define SA2_ADDR0 0x00 /* Etherexpress Pro/10+ */ +#define SA2_ADDR1 0xa0 +#define SA2_ADDR2 0xc9 + +#define SA3_ADDR0 0x00 /* more Etherexpress Pro/10+ */ +#define SA3_ADDR1 0xaa +#define SA3_ADDR2 0x00 +#define SA3_ADDR3 0xc9 + /* Index to functions, as function prototypes. */ extern int eepro_probe(struct device *dev); @@ -200,14 +221,18 @@ buffer (transmit-buffer = 32K - receive-buffer). */ + #define RAM_SIZE 0x8000 #define RCV_HEADER 8 #define RCV_RAM 0x6000 /* 24KB default for RCV buffer */ #define RCV_LOWER_LIMIT 0x00 /* 0x0000 */ + /* #define RCV_UPPER_LIMIT ((RCV_RAM - 2) >> 8) */ /* 0x5ffe */ #define RCV_UPPER_LIMIT (((rcv_ram) - 2) >> 8) + /* #define XMT_RAM (RAM_SIZE - RCV_RAM) */ /* 8KB for XMT buffer */ #define XMT_RAM (RAM_SIZE - (rcv_ram)) /* 8KB for XMT buffer */ + /* #define XMT_LOWER_LIMIT (RCV_RAM >> 8) */ /* 0x6000 */ #define XMT_LOWER_LIMIT ((rcv_ram) >> 8) #define XMT_UPPER_LIMIT ((RAM_SIZE - 2) >> 8) /* 0x7ffe */ @@ -228,6 +253,7 @@ #define BANK2_SELECT 0x80 /* Bank 0 registers */ + #define COMMAND_REG 0x00 /* Register 0 */ #define MC_SETUP 0x03 #define XMT_CMD 0x04 @@ -263,6 +289,7 @@ #define IO_PORT_32_BIT 0x0c /* Bank 1 registers */ + #define REG1 0x01 #define WORD_WIDTH 0x02 #define INT_ENABLE 0x80 @@ -273,6 +300,7 @@ #define XMT_UPPER_LIMIT_REG 0x0b /* Bank 2 registers */ + #define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */ #define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */ #define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */ @@ -285,7 +313,7 @@ #define REG13 0x0d #define FDX 0x00 #define A_N_ENABLE 0x02 - + #define I_ADD_REG0 0x04 #define I_ADD_REG1 0x05 #define I_ADD_REG2 0x06 @@ -299,20 +327,26 @@ #define EEDI 0x04 #define EEDO 0x08 +/* Check for a network adaptor of this type, and return '0' if one exists. -/* Check for a network adaptor of this type, and return '0' iff one exists. If dev->base_addr == 0, probe all likely locations. If dev->base_addr == 1, always return failure. If dev->base_addr == 2, allocate space for the device and return success (detachable devices only). + */ + #ifdef HAVE_DEVLIST + /* Support for a alternate probe manager, which will eliminate the boilerplate below. */ + struct netdev_entry netcard_drv = {"eepro", eepro_probe1, EEPRO_IO_EXTENT, eepro_portlist}; + #else -int + +int eepro_probe(struct device *dev) { int i; @@ -327,6 +361,7 @@ int ioaddr = eepro_portlist[i]; if (check_region(ioaddr, EEPRO_IO_EXTENT)) continue; + if (eepro_probe1(dev, ioaddr) == 0) return 0; } @@ -339,24 +374,23 @@ probes on the ISA bus. A good device probes avoids doing writes, and verifies that the correct device exists and functions. */ -int eepro_probe1(struct device *dev, short ioaddr) +int +eepro_probe1(struct device *dev, short ioaddr) { unsigned short station_addr[6], id, counter; int i; - int eepro; /* a flag, TRUE=1 for the EtherExpress Pro/10, - FALSE = 0 for other 82595-based lan cards. */ + int eepro; const char *ifmap[] = {"AUI", "10Base2", "10BaseT"}; enum iftype { AUI=0, BNC=1, TPE=2 }; /* Now, we are going to check for the signature of the ID_REG (register 2 of bank 0) */ - if (((id=inb(ioaddr + ID_REG)) & ID_REG_MASK) == ID_REG_SIG) { /* We seem to have the 82595 signature, let's play with its counter (last 2 bits of register 2 of bank 0) to be sure. */ - + counter = (id & R_ROBIN_BITS); if (((id=inb(ioaddr+ID_REG)) & R_ROBIN_BITS) == (counter + 0x40)) { @@ -372,44 +406,54 @@ /* Check the station address for the manufacturer's code */ - if (station_addr[2] != 0x00aa || (station_addr[1] & 0xff00) != 0x0000) { - eepro = 0; - printk("%s: Intel 82595-based lan card at %#x,", + if ((station_addr[2] == 0x00aa) && (station_addr[1]!= 0x00c9)) { + eepro = 1; + printk("%s: Intel EtherExpress Pro/10 ISA at %#x,", + dev->name, ioaddr); + } else + if ( (station_addr[2] == 0x00a0) + || ((station_addr[2] == 0x00aa) && (station_addr[1] == 0x00c9) )) { + eepro = 2; + printk("%s: Intel EtherExpress Pro/10+ ISA\n at %#x,", dev->name, ioaddr); } else { - eepro = 1; - printk("%s: Intel EtherExpress Pro/10 at %#x,", + eepro = 0; + printk("%s: Intel 82595-based lan card at %#x,", dev->name, ioaddr); } /* Fill in the 'dev' fields. */ dev->base_addr = ioaddr; - + for (i=0; i < 6; i++) { dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i]; printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); } - + if ((dev->mem_end & 0x3f) < 3 || /* RX buffer must be more than 3K */ (dev->mem_end & 0x3f) > 29) /* and less than 29K */ dev->mem_end = RCV_RAM; /* or it will be set to 24K */ else dev->mem_end = 1024*dev->mem_end; /* Maybe I should shift << 10 */ /* From now on, dev->mem_end contains the actual size of rx buffer */ - + if (net_debug > 3) printk(", %dK RCV buffer", (int)(dev->mem_end)/1024); - + outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ id = inb(ioaddr + REG3); if (id & TPE_BIT) dev->if_port = TPE; else dev->if_port = BNC; + if (net_debug>3) + printk("id: %x\n", id); + if (dev->irq < 2 && eepro) { i = read_eeprom(ioaddr, 1); - switch (i & 0x07) { + if (eepro == 1) + switch (i & 0x07) { case 0: dev->irq = 9; break; case 1: dev->irq = 3; break; case 2: dev->irq = 5; break; @@ -419,6 +463,16 @@ printk(" illegal interrupt vector stored in EEPROM.\n"); return ENODEV; } + else switch (i & 0x07) { + case 0: dev->irq = 3; break; + case 1: dev->irq = 4; break; + case 2: dev->irq = 5; break; + case 3: dev->irq = 7; break; + case 4: dev->irq = 9; break; + case 5: dev->irq = 10; break; + case 6: dev->irq = 11; break; + case 7: dev->irq = 12; break; + } } else if (dev->irq == 2) dev->irq = 9; @@ -432,7 +486,7 @@ } } else printk(", %s.\n", ifmap[dev->if_port]); - + if ((dev->mem_start & 0xf) > 0) /* I don't know if this is */ net_debug = dev->mem_start & 7; /* still useful or not */ @@ -486,9 +540,12 @@ */ static char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1}; -static int eepro_grab_irq(struct device *dev) +static char irqrmap2[] = {-1,-1,4,0,1,2,-1,3,-1,4,5,6,7,-1,-1,-1}; + +static int +eepro_grab_irq(struct device *dev) { - int irqlist[] = { 5, 9, 10, 11, 4, 3, 0}; + int irqlist[] = { 3, 4, 5, 7, 9, 10, 11, 12 }; int *irqp = irqlist, temp_reg, ioaddr = dev->base_addr; outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */ @@ -496,32 +553,28 @@ /* Enable the interrupt line. */ temp_reg = inb(ioaddr + REG1); outb(temp_reg | INT_ENABLE, ioaddr + REG1); - + outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */ /* clear all interrupts */ outb(ALL_MASK, ioaddr + STATUS_REG); + /* Let EXEC event to interrupt */ outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG); do { outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */ - temp_reg = inb(ioaddr + INT_NO_REG); outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG); - outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ - if (request_irq (*irqp, NULL, 0, "bogus", NULL) != EBUSY) { /* Twinkle the interrupt, and check if it's seen */ autoirq_setup(0); - outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */ if (*irqp == autoirq_report(2) && /* It's a good IRQ line */ (request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro", NULL) == 0)) break; - /* clear all interrupts */ outb(ALL_MASK, ioaddr + STATUS_REG); } @@ -532,7 +585,6 @@ /* Disable the physical interrupt line. */ temp_reg = inb(ioaddr + REG1); outb(temp_reg & 0x7f, ioaddr + REG1); - outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ /* Mask all the interrupts. */ @@ -554,10 +606,27 @@ if (net_debug > 3) printk("eepro: entering eepro_open routine.\n"); - if (dev->dev_addr[0] == SA_ADDR0 && + if ((dev->dev_addr[0] == SA_ADDR0 && dev->dev_addr[1] == SA_ADDR1 && - dev->dev_addr[2] == SA_ADDR2) - lp->eepro = 1; /* Yes, an Intel EtherExpress Pro/10 */ + dev->dev_addr[2] == SA_ADDR2)&& + (dev->dev_addr[3] != SA3_ADDR3)) + { + lp->eepro = 1; + if (net_debug > 3) printk("p->eepro = 1;\n"); + } /* Yes, an Intel EtherExpress Pro/10 */ + + else if ((dev->dev_addr[0] == SA2_ADDR0 && + dev->dev_addr[1] == SA2_ADDR1 && + dev->dev_addr[2] == SA2_ADDR2)|| + (dev->dev_addr[0] == SA3_ADDR0 && + dev->dev_addr[1] == SA3_ADDR1 && + dev->dev_addr[2] == SA3_ADDR2 && + dev->dev_addr[3] == SA3_ADDR3)) + { + lp->eepro = 2; /* Yes, an Intel EtherExpress Pro/10+ */ + if (net_debug > 3) printk("p->eepro = 2;\n"); + } + else lp->eepro = 0; /* No, it is a generic 82585 lan card */ /* Get the interrupt vector for the 82595 */ @@ -571,15 +640,12 @@ return -EAGAIN; /* Initialize the 82595. */ - outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ temp_reg = inb(ioaddr + EEPROM_REG); - lp->stepping = temp_reg >> 5; /* Get the stepping number of the 595 */ if (net_debug > 3) printk("The stepping of the 82595 is %d\n", lp->stepping); - if (temp_reg & 0x10) /* Check the TurnOff Enable bit */ outb(temp_reg & 0xef, ioaddr + EEPROM_REG); for (i=0; i < 6; i++) @@ -588,19 +654,31 @@ temp_reg = inb(ioaddr + REG1); /* Setup Transmit Chaining */ outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */ | RCV_Discard_BadFrame, ioaddr + REG1); - temp_reg = inb(ioaddr + REG2); /* Match broadcast */ outb(temp_reg | 0x14, ioaddr + REG2); - temp_reg = inb(ioaddr + REG3); outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */ /* Set the receiving mode */ outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */ + + /* Set the interrupt vector */ + temp_reg = inb(ioaddr + INT_NO_REG); + if (lp->eepro == 2) + outb((temp_reg & 0xf8) | irqrmap2[dev->irq], ioaddr + INT_NO_REG); + else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); + temp_reg = inb(ioaddr + INT_NO_REG); - outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); - + + if (lp->eepro == 2) + outb((temp_reg & 0xf0) | irqrmap2[dev->irq] | 0x08,ioaddr+INT_NO_REG); + else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); + + if (net_debug > 3) + printk("eepro_open: content of INT Reg is %x\n", temp_reg); + + /* Initialize the RCV and XMT upper and lower limits */ outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG); outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG); @@ -610,25 +688,26 @@ /* Enable the interrupt line. */ temp_reg = inb(ioaddr + REG1); outb(temp_reg | INT_ENABLE, ioaddr + REG1); - outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ - + /* Let RX and TX events to interrupt */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); + /* clear all interrupts */ outb(ALL_MASK, ioaddr + STATUS_REG); - + /* Initialize RCV */ outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR); lp->rx_start = (RCV_LOWER_LIMIT << 8) ; outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP); - + /* Initialize XMT */ outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR); - + /* Check for the i82595TX and i82595FX */ old8 = inb(ioaddr + 8); outb(~old8, ioaddr + 8); + if ((temp_reg = inb(ioaddr + 8)) == old8) { if (net_debug > 3) printk("i82595 detected!\n"); @@ -639,45 +718,55 @@ outb(old8, ioaddr + 8); old9 = inb(ioaddr + 9); outb(~old9, ioaddr + 9); - if ((temp_reg = inb(ioaddr + 9)) == ~old9) { + + if (((temp_reg = inb(ioaddr + 9)) == ( (~old9)&0xff) )) { enum iftype { AUI=0, BNC=1, TPE=2 }; - if (net_debug > 3) + + if (net_debug > 3) { + printk("temp_reg: %#x ~old9: %#x\n",temp_reg, ~old9); printk("i82595FX detected!\n"); + } + lp->version = LAN595FX; outb(old9, ioaddr + 9); + if (dev->if_port != TPE) { /* Hopefully, this will fix the problem of using Pentiums and pro/10 w/ BNC. */ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ temp_reg = inb(ioaddr + REG13); + /* disable the full duplex mode since it is not applicable with the 10Base2 cable. */ outb(temp_reg & ~(FDX | A_N_ENABLE), REG13); outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */ } } - else if (net_debug > 3) + else if (net_debug > 3) { + printk("temp_reg: %#x ~old9: %#x\n",temp_reg,((~old9)&0xff)); printk("i82595TX detected!\n"); + } } outb(SEL_RESET_CMD, ioaddr); + /* We are supposed to wait for 2 us after a SEL_RESET */ SLOW_DOWN_IO; SLOW_DOWN_IO; - + lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */ lp->tx_last = 0; dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; - + if (net_debug > 3) printk("eepro: exiting eepro_open routine.\n"); - + outb(RCV_ENABLE_CMD, ioaddr); - MOD_INC_USE_COUNT; + return 0; } @@ -694,64 +783,68 @@ if (dev->tbusy) { /* If we get here, some higher level has decided we are broken. There should really be a "kick me" function call instead. */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 40) return 1; + if (net_debug > 1) printk("%s: transmit timed out, %s?\n", dev->name, "network cable problem"); + lp->stats.tx_errors++; - + /* Try to restart the adaptor. */ outb(SEL_RESET_CMD, ioaddr); + /* We are supposed to wait for 2 us after a SEL_RESET */ SLOW_DOWN_IO; SLOW_DOWN_IO; - + /* Do I also need to flush the transmit buffers here? YES? */ lp->tx_start = lp->tx_end = rcv_ram; lp->tx_last = 0; dev->tbusy=0; dev->trans_start = jiffies; - outb(RCV_ENABLE_CMD, ioaddr); - } - + /* If some higher layer thinks we've missed an tx-done interrupt we are passed NULL. Caution: dev_tint() handles the cli()/sti() itself. */ + if (skb == NULL) { dev_tint(dev); return 0; } - + /* Block a timer-based transmit from overlapping. */ + if (set_bit(0, (void*)&dev->tbusy) != 0) printk("%s: Transmitter access conflict.\n", dev->name); else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; - hardware_send_packet(dev, buf, length); dev->trans_start = jiffies; } - + dev_kfree_skb (skb, FREE_WRITE); - + /* You might need to clean up and record Tx statistics here. */ /* lp->stats.tx_aborted_errors++; */ - + if (net_debug > 5) printk("eepro: exiting eepro_send_packet routine.\n"); return 0; } - /* The typical workload of the driver: Handle the network interface interrupts. */ + static void eepro_interrupt(int irq, void *dev_id, struct pt_regs * regs) { @@ -765,38 +858,35 @@ printk ("eepro_interrupt(): irq %d for unknown device.\n", irq); return; } + dev->interrupt = 1; ioaddr = dev->base_addr; - + do { status = inb(ioaddr + STATUS_REG); if (status & RX_INT) { if (net_debug > 4) printk("eepro: packet received interrupt.\n"); - /* Acknowledge the RX_INT */ outb(RX_INT, ioaddr + STATUS_REG); - /* Get the received packets */ eepro_rx(dev); } - else if (status & TX_INT) { if (net_debug > 4) printk("eepro: packet transmit interrupt.\n"); - /* Acknowledge the TX_INT */ outb(TX_INT, ioaddr + STATUS_REG); - /* Process the status of transmitted packets */ eepro_transmit_interrupt(dev); } } while ((boguscount-- > 0) && (status & 0x06)); - + dev->interrupt = 0; + if (net_debug > 5) printk("eepro: exiting eepro_interrupt routine.\n"); @@ -813,41 +903,39 @@ dev->tbusy = 1; dev->start = 0; - + outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */ - + /* Disable the physical interrupt line. */ temp_reg = inb(ioaddr + REG1); outb(temp_reg & 0x7f, ioaddr + REG1); - outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ - + /* Flush the Tx and disable Rx. */ outb(STOP_RCV_CMD, ioaddr); + lp->tx_start = lp->tx_end = rcv_ram ; lp->tx_last = 0; - + /* Mask all the interrupts. */ outb(ALL_MASK, ioaddr + INT_MASK_REG); - + /* clear all interrupts */ outb(ALL_MASK, ioaddr + STATUS_REG); - + /* Reset the 82595 */ outb(RESET_CMD, ioaddr); - + /* release the interrupt */ free_irq(dev->irq, NULL); - irq2dev_map[dev->irq] = 0; - + /* Update the statistics here. What statistics? */ - /* We are supposed to wait for 200 us after a RESET */ SLOW_DOWN_IO; SLOW_DOWN_IO; /* May not be enough? */ - MOD_DEC_USE_COUNT; + return 0; } @@ -857,12 +945,12 @@ eepro_get_stats(struct device *dev) { struct eepro_local *lp = (struct eepro_local *)dev->priv; - return &lp->stats; } /* Set or clear the multicast filter for this adaptor. */ + static void set_multicast_list(struct device *dev) { @@ -870,7 +958,7 @@ short ioaddr = dev->base_addr; unsigned short mode; struct dev_mc_list *dmi=dev->mc_list; - + if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63) { /* @@ -880,7 +968,6 @@ * flag is already set. If not we assert it. */ dev->flags|=IFF_PROMISC; - outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ mode = inb(ioaddr + REG2); outb(mode | PRMSC_Mode, ioaddr + REG2); @@ -889,6 +976,7 @@ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ printk("%s: promiscuous mode enabled.\n", dev->name); } + else if (dev->mc_count==0 ) { outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ @@ -898,6 +986,7 @@ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ } + else { unsigned short status, *eaddrs; @@ -907,7 +996,6 @@ corruption of the HOST_ADDRESS_REG by interrupt service routines. */ outb(ALL_MASK, ioaddr + INT_MASK_REG); - outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ mode = inb(ioaddr + REG2); outb(mode | Multi_IA, ioaddr + REG2); @@ -919,6 +1007,7 @@ outw(0, ioaddr + IO_PORT); outw(0, ioaddr + IO_PORT); outw(6*(dev->mc_count + 1), ioaddr + IO_PORT); + for (i = 0; i < dev->mc_count; i++) { eaddrs=(unsigned short *)dmi->dmi_addr; @@ -927,15 +1016,17 @@ outw(*eaddrs++, ioaddr + IO_PORT); outw(*eaddrs++, ioaddr + IO_PORT); } + eaddrs = (unsigned short *) dev->dev_addr; outw(eaddrs[0], ioaddr + IO_PORT); outw(eaddrs[1], ioaddr + IO_PORT); outw(eaddrs[2], ioaddr + IO_PORT); outw(lp->tx_end, ioaddr + XMT_BAR); outb(MC_SETUP, ioaddr); - + /* Update the transmit queue */ i = lp->tx_end + XMT_HEADER + 6*(dev->mc_count + 1); + if (lp->tx_start != lp->tx_end) { /* update the next address and the chain bit in the @@ -950,15 +1041,17 @@ else { lp->tx_start = lp->tx_end = i ; } - + /* Acknowledge that the MC setup is done */ do { /* We should be doing this in the eepro_interrupt()! */ SLOW_DOWN_IO; SLOW_DOWN_IO; + if (inb(ioaddr + STATUS_REG) & 0x08) { i = inb(ioaddr); outb(0x08, ioaddr + STATUS_REG); + if (i & 0x20) { /* command ABORTed */ printk("%s: multicast setup failed.\n", dev->name); @@ -970,7 +1063,7 @@ } } } while (++boguscount < 100); - + /* Re-enable RX and TX interrupts */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); @@ -980,8 +1073,8 @@ /* The horrible routine to read a word from the serial EEPROM. */ /* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */ - /* The delay between EEPROM clock transitions. */ + #define eeprom_delay() { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} #define EE_READ_CMD (6 << 6) @@ -1014,7 +1107,6 @@ retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); outb(ctrl_val, ee_addr); eeprom_delay(); } - /* Terminate the EEPROM access. */ ctrl_val &= ~EECS; outb(ctrl_val | EESK, ee_addr); @@ -1035,47 +1127,47 @@ if (net_debug > 5) printk("eepro: entering hardware_send_packet routine.\n"); - + while (boguscount-- > 0) { - + /* Disable RX and TX interrupts. Necessary to avoid corruption of the HOST_ADDRESS_REG by interrupt service routines. */ outb(ALL_MASK, ioaddr + INT_MASK_REG); - + if (dev->interrupt == 1) { /* Enable RX and TX interrupts */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); continue; } - + /* determine how much of the transmit buffer space is available */ if (lp->tx_end > lp->tx_start) tx_available = XMT_RAM - (lp->tx_end - lp->tx_start); else if (lp->tx_end < lp->tx_start) tx_available = lp->tx_start - lp->tx_end; else tx_available = XMT_RAM; - + if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER) >= tx_available) /* No space available ??? */ { eepro_transmit_interrupt(dev); /* Clean up the transmiting queue */ - /* Enable RX and TX interrupts */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); continue; } - + last = lp->tx_end; end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; - if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */ + if ((RAM_SIZE - last) <= XMT_HEADER) { /* Arrrr!!!, must keep the xmt header together, several days were lost to chase this one down. */ last = rcv_ram; end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; } + else end = rcv_ram + (end - RAM_SIZE); } @@ -1084,19 +1176,20 @@ outw(0, ioaddr + IO_PORT); outw(end, ioaddr + IO_PORT); outw(length, ioaddr + IO_PORT); - + if (lp->version == LAN595) outsw(ioaddr + IO_PORT, buf, (length + 3) >> 1); + else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */ unsigned short temp = inb(ioaddr + INT_MASK_REG); outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG); outsl(ioaddr + IO_PORT_32_BIT, buf, (length + 3) >> 2); outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG); } - + /* A dummy read to flush the DRAM write pipeline */ status = inw(ioaddr + IO_PORT); - + if (lp->tx_start == lp->tx_end) { outw(last, ioaddr + XMT_BAR); outb(XMT_CMD, ioaddr); @@ -1105,34 +1198,36 @@ else { /* update the next address and the chain bit in the last packet */ + if (lp->tx_end != last) { outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG); outw(last, ioaddr + IO_PORT); } + outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG); status = inw(ioaddr + IO_PORT); outw(status | CHAIN_BIT, ioaddr + IO_PORT); - + /* Continue the transmit command */ outb(RESUME_XMT_CMD, ioaddr); } - lp->tx_last = last; lp->tx_end = end; - + /* Enable RX and TX interrupts */ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); if (dev->tbusy) { dev->tbusy = 0; } - + if (net_debug > 5) printk("eepro: exiting hardware_send_packet routine.\n"); + return; } - dev->tbusy = 1; + if (net_debug > 5) printk("eepro: exiting hardware_send_packet routine.\n"); } @@ -1151,68 +1246,78 @@ /* Set the read pointer to the start of the RCV */ outw(rcv_car, ioaddr + HOST_ADDRESS_REG); + rcv_event = inw(ioaddr + IO_PORT); - while (rcv_event == RCV_DONE) { + rcv_status = inw(ioaddr + IO_PORT); rcv_next_frame = inw(ioaddr + IO_PORT); rcv_size = inw(ioaddr + IO_PORT); - + if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) { + /* Malloc up new buffer. */ struct sk_buff *skb; - rcv_size &= 0x3fff; skb = dev_alloc_skb(rcv_size+5); + if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; break; } + skb->dev = dev; skb_reserve(skb,2); - + if (lp->version == LAN595) insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1); + else { /* LAN595TX or LAN595FX, capable of 32-bit I/O processing */ unsigned short temp = inb(ioaddr + INT_MASK_REG); outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG); insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size), (rcv_size + 3) >> 2); outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG); } - skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); lp->stats.rx_packets++; } + else { /* Not sure will ever reach here, I set the 595 to discard bad received frames */ lp->stats.rx_errors++; + if (rcv_status & 0x0100) lp->stats.rx_over_errors++; + else if (rcv_status & 0x0400) lp->stats.rx_frame_errors++; + else if (rcv_status & 0x0800) lp->stats.rx_crc_errors++; + printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n", dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size); } + if (rcv_status & 0x1000) lp->stats.rx_length_errors++; + if (--boguscount == 0) break; - + rcv_car = lp->rx_start + RCV_HEADER + rcv_size; lp->rx_start = rcv_next_frame; outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG); rcv_event = inw(ioaddr + IO_PORT); - } if (rcv_car == 0) rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff; + outw(rcv_car - 1, ioaddr + RCV_STOP); - + if (net_debug > 5) printk("eepro: exiting eepro_rx routine.\n"); } @@ -1226,17 +1331,17 @@ short xmt_status; while (lp->tx_start != lp->tx_end) { - + outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG); xmt_status = inw(ioaddr+IO_PORT); + if ((xmt_status & TX_DONE_BIT) == 0) break; - + xmt_status = inw(ioaddr+IO_PORT); lp->tx_start = inw(ioaddr+IO_PORT); - dev->tbusy = 0; mark_bh(NET_BH); - + if (xmt_status & 0x2000) lp->stats.tx_packets++; else { @@ -1246,41 +1351,45 @@ printk("%s: XMT status = %#x\n", dev->name, xmt_status); } + if (xmt_status & 0x000f) { lp->stats.collisions += (xmt_status & 0x000f); } + if ((xmt_status & 0x0040) == 0x0) { lp->stats.tx_heartbeat_errors++; } - + if (--boguscount == 0) break; } } #ifdef MODULE + static char devicename[9] = { 0, }; static struct device dev_eepro = { devicename, /* device name is inserted by linux/drivers/net/net_init.c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, eepro_probe }; - static int io = 0x200; static int irq = 0; static int mem = (RCV_RAM/1024); /* Size of the rx buffer in KB */ -int +int init_module(void) { if (io == 0) printk("eepro: You should not use auto-probing with insmod!\n"); + dev_eepro.base_addr = io; dev_eepro.irq = irq; dev_eepro.mem_end = mem; if (register_netdev(&dev_eepro) != 0) return -EIO; + return 0; } @@ -1288,6 +1397,7 @@ cleanup_module(void) { unregister_netdev(&dev_eepro); + kfree_s(dev_eepro.priv,sizeof(struct eepro_local)); dev_eepro.priv=NULL; diff -u --recursive --new-file v2.0.35/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c --- v2.0.35/linux/drivers/net/eepro100.c Sun Nov 15 10:49:38 1998 +++ linux/drivers/net/eepro100.c Sun Nov 15 10:33:02 1998 @@ -41,7 +41,6 @@ /* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */ static int multicast_filter_limit = 64; -#include #ifdef MODULE #ifdef MODVERSIONS #include @@ -182,10 +181,10 @@ doing the CU_RESUME the chip processes the next-yet-valid post-final-command. So blindly sending a CU_RESUME is only safe if we do it immediately after -after erasing the previous CmdSuspend, without the possibility of an -intervening delay. Thus the resume command is always within the -interrupts-disabled region. This is a timing dependence, but handling this -condition in a timing-independent way would considerably complicate the code. +erasing the previous CmdSuspend, without the possibility of an intervening +delay. Thus the resume command is always within the interrupts-disabled +region. This is a timing dependence, but handling this condition in a +timing-independent way would considerably complicate the code. Note: In previous generation Intel chips, restarting the command unit was a notoriously slow process. This is presumably no longer true. diff -u --recursive --new-file v2.0.35/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c --- v2.0.35/linux/drivers/net/eexpress.c Tue Apr 8 08:47:45 1997 +++ linux/drivers/net/eexpress.c Sun Nov 15 10:33:03 1998 @@ -7,6 +7,11 @@ * Changed to support io= irq= by Alan Cox * Reworked 1995 by John Sullivan * More fixes by Philip Blundell + * Added the Compaq LTE Alan Cox + * + * Note - this driver is experimental still - it has problems on faster + * machines. Someone needs to sit down and go through it line by line with + * a databook... */ /* @@ -86,7 +91,8 @@ static char version[] = "eexpress.c: v0.10 04-May-95 John Sullivan \n" -" v0.14 19-May-96 Philip Blundell \n"; +" v0.14 19-May-96 Philip Blundell \n" +" v0.15 04-Aug-98 Alan Cox \n"; #include @@ -701,11 +707,13 @@ hw_addr[1] = eexp_hw_readeeprom(ioaddr,3); hw_addr[2] = eexp_hw_readeeprom(ioaddr,4); - if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000)) + /* Standard Address or Compaq LTE Address */ + if (!((hw_addr[2]==0x00aa && ((hw_addr[1] & 0xff00)==0x0000)) || + (hw_addr[2]==0x0080 && ((hw_addr[1] & 0xff00)==0x5F00)))) { printk("rejected: invalid address %04x%04x%04x\n", hw_addr[2],hw_addr[1],hw_addr[0]); - return ENODEV; + return -ENODEV; } dev->base_addr = ioaddr; diff -u --recursive --new-file v2.0.35/linux/drivers/net/epic100.c linux/drivers/net/epic100.c --- v2.0.35/linux/drivers/net/epic100.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/epic100.c Sun Nov 15 10:33:03 1998 @@ -1,10 +1,10 @@ -/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Linux. */ +/* epic100.c: A SMC 83c170 EPIC/100 Fast Ethernet driver for Linux. */ /* Written 1997-1998 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - All other rights reserved. + All other rights reserved. This driver is for the SMC83c170/175 "EPIC" series, as used on the SMC EtherPower II 9432 PCI adapter, and several CardBus cards. @@ -18,7 +18,7 @@ */ static const char *version = -"epic100.c:v0.99B 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n"; +"epic100.c:v1.03 8/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n"; /* A few user-configurable values. */ @@ -43,16 +43,16 @@ #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ /* Bytes transferred to chip before transmission starts. */ -#define TX_FIFO_THRESH 128 /* Rounded down to 4 byte units. */ +#define TX_FIFO_THRESH 256 /* Rounded down to 4 byte units. */ #define RX_FIFO_THRESH 1 /* 0-3, 0==32, 64,96, or 3==128 bytes */ #include +#include /* Evil, but neccessary */ #ifdef MODULE #ifdef MODVERSIONS #include #endif #include -#include #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT @@ -68,7 +68,11 @@ #include #include #include +#if LINUX_VERSION_CODE >= 0x20155 +#define PCI_SUPPORT_VER2 +#else #include +#endif #include #include /* Processor type for cache alignment. */ @@ -80,11 +84,15 @@ #include #include -#define RUN_AT(x) (jiffies + (x)) +/* Kernel compatibility defines, common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ -#if LINUX_VERSION_CODE < 0x20138 -#define test_and_set_bit(val, addr) set_bit(val, addr) +#if ! defined (LINUX_VERSION_CODE) || LINUX_VERSION_CODE < 0x20000 +#warning This driver version is only for kernel versions 2.0.0 and later. #endif + +#define RUN_AT(x) (jiffies + (x)) + #if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115) MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver"); @@ -94,6 +102,18 @@ MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(max_interrupt_work, "i"); #endif +#if LINUX_VERSION_CODE < 0x20123 +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#define NETSTATS_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define DEV_FREE_SKB(skb) dev_kfree_skb(skb, FREE_WRITE); +#else /* Grrr, unneeded incompatible change. */ +#define DEV_FREE_SKB(skb) dev_kfree_skb(skb); +#endif /* The I/O extent. */ #define EPIC_TOTAL_SIZE 0x100 @@ -106,7 +126,7 @@ I. Board Compatibility This device driver is designed for the SMC "EPCI/100", the SMC -single-chip ethernet controllers for PCI. This chip is used on +single-chip Ethernet controllers for PCI. This chip is used on the SMC EtherPower II boards. @@ -132,17 +152,31 @@ */ -#ifndef PCI_VENDOR_ID_SMC -#define PCI_VENDOR_ID_SMC 0x10B8 -#endif -#ifndef PCI_DEVICE_ID_SMC_EPIC100 -#define PCI_DEVICE_ID_SMC_EPIC100 0x0005 -#endif - /* The rest of these values should never change. */ + +static struct device *epic_probe1(int pci_bus, int pci_devfn, + struct device *dev, int card_idx); + +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +struct chip_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, pci_flags; + int io_size, min_latency; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + int chip_idx); +} chip_tbl[] = { + {"SMSC EPIC/100", 0x10B8, 0x0005, 0x7fff, + PCI_USES_IO|PCI_USES_MASTER|PCI_ADDR0, EPIC_TOTAL_SIZE, 32, epic_probe1}, + {0,}, +}; + /* Offsets to registers, using the (ugh) SMC names. */ enum epic_registers { COMMAND=0, INTSTAT=4, INTMASK=8, GENCTL=0x0C, NVCTL=0x10, EECTL=0x14, + PCIBurstCnt=0x18, TEST1=0x1C, CRCCNT=0x20, ALICNT=0x24, MPCNT=0x28, /* Rx error counters. */ MIICtrl=0x30, MIIData=0x34, MIICfg=0x38, LAN0=64, /* MAC address. */ @@ -153,8 +187,9 @@ /* Interrupt register bits, using my own meaningful names. */ enum IntrStatus { - TxIdle=0x40000, RxIdle=0x20000, - CntFull=0x0200, TxUnderrun=0x0100, + TxIdle=0x40000, RxIdle=0x20000, IntrSummary=0x010000, + PCIBusErr170=0x7000, PCIBusErr175=0x1000, PhyEvent175=0x8000, + RxStarted=0x0800, RxEarlyWarn=0x0400, CntFull=0x0200, TxUnderrun=0x0100, TxEmpty=0x0080, TxDone=0x0020, RxError=0x0010, RxOverflow=0x0008, RxFull=0x0004, RxHeader=0x0002, RxDone=0x0001, }; @@ -167,7 +202,7 @@ u32 bufaddr; u16 buflength; u16 control; - u32 next; + u32 next; }; struct epic_rx_desc { @@ -175,7 +210,7 @@ u16 rxlength; u32 bufaddr; u32 buflength; - u32 next; + u32 next; }; struct epic_private { @@ -198,12 +233,13 @@ u8 pci_bus, pci_dev_fn; /* PCI bus location. */ u16 chip_id; - struct enet_statistics stats; + struct net_device_stats stats; struct timer_list timer; /* Media selection timer. */ unsigned char mc_filter[8]; signed char phys[4]; /* MII device addresses. */ unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int full_duplex:1; /* Current duplex setting. */ + unsigned int force_fd:1; /* Full-duplex operation requested. */ unsigned int default_port:4; /* Last dev->if_port value. */ unsigned int media2:4; /* Secondary monitored media port. */ unsigned int medialock:1; /* Don't sense media type. */ @@ -216,119 +252,140 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static struct device *epic100_probe1(int pci_bus, int pci_devfn, - struct device *dev, int card_idx); static int epic_open(struct device *dev); -static int read_eeprom(int ioaddr, int location); -static int mii_read(int ioaddr, int phy_id, int location); +static int read_eeprom(long ioaddr, int location); +static int mdio_read(long ioaddr, int phy_id, int location); +static void mdio_write(long ioaddr, int phy_id, int location, int value); +static void epic_restart(struct device *dev); static void epic_timer(unsigned long data); static void epic_tx_timeout(struct device *dev); static void epic_init_ring(struct device *dev); static int epic_start_xmit(struct sk_buff *skb, struct device *dev); static int epic_rx(struct device *dev); static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd); static int epic_close(struct device *dev); -static struct enet_statistics *epic_get_stats(struct device *dev); +static struct net_device_stats *epic_get_stats(struct device *dev); static void set_rx_mode(struct device *dev); /* A list of all installed EPIC devices, for removing the driver module. */ static struct device *root_epic_dev = NULL; +#ifndef CARDBUS int epic100_probe(struct device *dev) { - static int cards_found = 0; - static int pci_index = 0; /* Static, for multiple probe calls. */ - - /* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well with the current structure. So instead we detect just the - Epic cards in slot order. */ - - if (pcibios_present()) { - unsigned char pci_bus, pci_device_fn; - - for (;pci_index < 0xff; pci_index++) { - u8 pci_latency; - u16 pci_command, new_command, vendor, device; - u32 pci_ioaddr; - - if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, -#ifdef REVERSE_PROBE_ORDER - 0xff - pci_index, + int cards_found = 0; + int chip_idx; + u16 pci_command, new_command; + unsigned char pci_bus, pci_device_fn; + +#ifdef PCI_SUPPORT_VER2 + struct pci_dev *pcidev = NULL; + while ((pcidev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pcidev)) + != NULL) { + long pci_ioaddr = pcidev->base_address[0] & ~3; + int vendor = pcidev->vendor; + int device = pcidev->device; + + for (chip_idx = 0; chip_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == chip_tbl[chip_idx].vendor_id + && (device & chip_tbl[chip_idx].device_id_mask) == + chip_tbl[chip_idx].device_id) + break; + if (chip_tbl[chip_idx].vendor_id == 0 /* Compiled out! */ + || check_region(pci_ioaddr, chip_tbl[chip_idx].io_size)) + continue; + pci_bus = pcidev->bus->number; + pci_device_fn = pcidev->devfn; #else - pci_index, -#endif - &pci_bus, &pci_device_fn) - != PCIBIOS_SUCCESSFUL) + int pci_index; + + if ( ! pcibios_present()) + return -ENODEV; + + for (pci_index = 0; pci_index < 0xff; pci_index++) { + u16 vendor, device; + u32 pci_ioaddr; + + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, + pci_index, &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + + for (chip_idx = 0; chip_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == chip_tbl[chip_idx].vendor_id + && (device & chip_tbl[chip_idx].device_id_mask) == + chip_tbl[chip_idx].device_id) break; - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_DEVICE_ID, &device); - if (vendor != PCI_VENDOR_ID_SMC) - continue; - if (device != PCI_DEVICE_ID_SMC_EPIC100) { - printk("Unknown SMC PCI ethernet chip type %4.4x detected:" - " not configured.\n", device); - continue; - } + if (chip_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ + continue; - /* Activate the card: fix for brain-damaged Win98 BIOSes. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled Ethernet" - " device %4.4x-%4.4x." - " Updating PCI command %4.4x->%4.4x.\n", - vendor, device, pci_command, new_command); - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; + + if (check_region(pci_ioaddr, chip_tbl[chip_idx].io_size)) + continue; +#endif + + /* EPIC-specific code: Soft-reset the chip ere setting as master. */ + outl(0x0001, pci_ioaddr + GENCTL); + + /* Activate the card: fix for brain-damaged Win98 BIOSes. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled Ethernet" + " device %4.4x-%4.4x." + " Updating PCI command %4.4x->%4.4x.\n", + vendor, device, pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + dev = chip_tbl[chip_idx].probe1(pci_bus, pci_device_fn, + dev, cards_found); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &pci_ioaddr); - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; - - if (check_region(pci_ioaddr, EPIC_TOTAL_SIZE)) - continue; - - dev = epic100_probe1(pci_bus, pci_device_fn, dev, cards_found); - - if (dev) { - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 32) { - printk(" PCI latency timer (CFLT) value of %d is " - "unreasonably low, setting to 32.\n", pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 32); - } else if (epic_debug > 1) - printk(" PCI latency timer (CFLT) is %#x.\n", - pci_latency); - dev = 0; - cards_found++; + /* Check the latency timer. */ + if (dev) { + u8 pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < chip_tbl[chip_idx].min_latency) { + printk(KERN_INFO " PCI latency timer (CFLT) value of %d is " + "unreasonably low, setting to %d.\n", pci_latency, + chip_tbl[chip_idx].min_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, + chip_tbl[chip_idx].min_latency); } + dev = 0; + cards_found++; } } return cards_found ? 0 : -ENODEV; } +#endif /* not CARDBUS */ -static struct device *epic100_probe1(int bus, int devfn, struct device *dev, +static struct device *epic_probe1(int bus, int devfn, struct device *dev, int card_idx) { static int did_version = 0; /* Already printed version info. */ - struct epic_private *tp; + struct epic_private *ep; int i, option = 0, duplex = 0; - u8 irq; u16 chip_id; u32 ioaddr; if (epic_debug > 0 && did_version++ == 0) - printk(version); + printk(KERN_INFO "%s", version); if (dev && dev->mem_start) { option = dev->mem_start; @@ -342,18 +399,38 @@ dev = init_etherdev(dev, 0); - pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &ioaddr); - pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); - pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &chip_id); - ioaddr &= ~3; + { /* Grrrr, badly consider interface change. */ +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(bus, devfn); + ioaddr = pdev->base_address[0] & ~3; + dev->irq = pdev->irq; + chip_id = pdev->device; +#else + u8 irq; + u32 ioaddr0; + pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &ioaddr0); + pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); + pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &chip_id); + ioaddr = ioaddr0 & ~3; + dev->irq = irq; +#endif + } - printk("%s: SMC EPIC/100 at %#3x, IRQ %d, ", dev->name, ioaddr, irq); + dev->base_addr = ioaddr; + printk(KERN_INFO "%s: SMC EPIC/100 (chip ID %4.4x) at %#3x, IRQ %d, ", + dev->name, chip_id, ioaddr, dev->irq); /* Bring the chip out of low-power mode. */ - outl(0x0200, ioaddr + GENCTL); + outl(0x4200, ioaddr + GENCTL); /* Magic?! If we don't set this bit the MII interface won't work. */ outl(0x0008, ioaddr + TEST1); + /* Turn on the MII transceiver. */ + outl(0x12, ioaddr + MIICfg); + if (chip_id == 6) + outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); + outl(0x0200, ioaddr + GENCTL); + /* This could also be read from the EEPROM. */ for (i = 0; i < 3; i++) ((u16 *)dev->dev_addr)[i] = inw(ioaddr + LAN0 + i*4); @@ -363,57 +440,64 @@ printk("%2.2x.\n", dev->dev_addr[i]); if (epic_debug > 1) { - printk("%s: EEPROM contents\n", dev->name); - for (i = 0; i < 64; i++) - printk(" %4.4x%s", read_eeprom(ioaddr, i), i % 16 == 15 ? "\n" : ""); + printk(KERN_DEBUG "%s: EEPROM contents\n", dev->name); + for (i = 0; i < 64; i++) + printk(" %4.4x%s", read_eeprom(ioaddr, i), + i % 16 == 15 ? "\n" : ""); } /* We do a request_region() to register /proc/ioports info. */ request_region(ioaddr, EPIC_TOTAL_SIZE, "SMC EPIC/100"); - dev->base_addr = ioaddr; - dev->irq = irq; - /* The data structures must be quadword aligned. */ - tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA); - memset(tp, 0, sizeof(*tp)); - dev->priv = tp; + ep = kmalloc(sizeof(*ep), GFP_KERNEL | GFP_DMA); + memset(ep, 0, sizeof(*ep)); + dev->priv = ep; - tp->next_module = root_epic_dev; + ep->next_module = root_epic_dev; root_epic_dev = dev; - tp->chip_id = chip_id; + ep->pci_bus = bus; + ep->pci_dev_fn = devfn; + ep->chip_id = chip_id; /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes too much time. */ { int phy, phy_idx; - for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); + for (phy = 1, phy_idx = 0; phy < 32 && phy_idx < sizeof(ep->phys); phy++) { - int mii_status = mii_read(ioaddr, phy, 0); + int mii_status = mdio_read(ioaddr, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { - tp->phys[phy_idx++] = phy; - printk("%s: MII transceiver found at address %d.\n", - dev->name, phy); + ep->phys[phy_idx++] = phy; + printk(KERN_INFO "%s: MII transceiver #%d control " + "%4.4x status %4.4x.\n" + KERN_INFO "%s: Autonegotiation advertising %4.4x " + "link partner %4.4x.\n", + dev->name, phy, mdio_read(ioaddr, phy, 0), mii_status, + dev->name, mdio_read(ioaddr, phy, 4), + mdio_read(ioaddr, phy, 5)); } } if (phy_idx == 0) { - printk("%s: ***WARNING***: No MII transceiver found!\n", + printk(KERN_WARNING "%s: ***WARNING***: No MII transceiver found!\n", dev->name); /* Use the known PHY address of the EPII. */ - tp->phys[0] = 3; + ep->phys[0] = 3; } } - /* Leave the chip in low-power mode. */ + /* Turn off the MII xcvr (175 only!), leave the chip in low-power mode. */ + if (ep->chip_id == 6) + outl(inl(ioaddr + NVCTL) & ~0x483C, ioaddr + NVCTL); outl(0x0008, ioaddr + GENCTL); /* The lower four bits are the media type. */ - tp->full_duplex = duplex; - tp->default_port = option; - if (tp->default_port) - tp->medialock = 1; + ep->force_fd = duplex; + ep->default_port = option; + if (ep->default_port) + ep->medialock = 1; /* The Epic-specific entries in the device structure. */ dev->open = &epic_open; @@ -421,6 +505,7 @@ dev->stop = &epic_close; dev->get_stats = &epic_get_stats; dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &mii_ioctl; return dev; } @@ -448,31 +533,32 @@ /* The EEPROM commands include the alway-set leading bit. */ #define EE_WRITE_CMD (5 << 6) -#define EE_READ_CMD (6 << 6) +#define EE_READ64_CMD (6 << 6) +#define EE_READ256_CMD (6 << 8) #define EE_ERASE_CMD (7 << 6) -static int read_eeprom(int ioaddr, int location) +static int read_eeprom(long ioaddr, int location) { int i; int retval = 0; - int ee_addr = ioaddr + EECTL; - int read_cmd = location | EE_READ_CMD; - + long ee_addr = ioaddr + EECTL; + int read_cmd = location | + (inl(ee_addr) & 0x40) ? EE_READ64_CMD : EE_READ256_CMD; + + printk("EEctrl is %x.\n", inl(ee_addr)); outl(EE_ENB & ~EE_CS, ee_addr); outl(EE_ENB, ee_addr); - + /* Shift the read command bits out. */ - for (i = 10; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + for (i = 12; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_WRITE_1 : EE_WRITE_0; outl(EE_ENB | dataval, ee_addr); eeprom_delay(100); outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); eeprom_delay(150); - outl(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ - eeprom_delay(250); } outl(EE_ENB, ee_addr); - + for (i = 16; i > 0; i--) { outl(EE_ENB | EE_SHIFT_CLK, ee_addr); eeprom_delay(100); @@ -488,7 +574,7 @@ #define MII_READOP 1 #define MII_WRITEOP 2 -static int mii_read(int ioaddr, int phy_id, int location) +static int mdio_read(long ioaddr, int phy_id, int location) { int i; @@ -496,22 +582,35 @@ /* Typical operation takes < 50 ticks. */ for (i = 4000; i > 0; i--) if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0) + return inw(ioaddr + MIIData); + return 0xffff; +} + +static void mdio_write(long ioaddr, int phy_id, int location, int value) +{ + int i; + + outw(value, ioaddr + MIIData); + outl((phy_id << 9) | (location << 4) | MII_WRITEOP, ioaddr + MIICtrl); + for (i = 10000; i > 0; i--) { + if ((inl(ioaddr + MIICtrl) & MII_WRITEOP) == 0) break; - return inw(ioaddr + MIIData); + } + return; } static int epic_open(struct device *dev) { - struct epic_private *tp = (struct epic_private *)dev->priv; - int ioaddr = dev->base_addr; + struct epic_private *ep = (struct epic_private *)dev->priv; + long ioaddr = dev->base_addr; int i; int mii_reg5; - int full_duplex = 0; + ep->full_duplex = ep->force_fd; /* Soft reset the chip. */ - outl(0x0001, ioaddr + GENCTL); + outl(0x4001, ioaddr + GENCTL); if (request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, "SMC EPIC/100", dev)) return -EAGAIN; @@ -520,32 +619,45 @@ epic_init_ring(dev); - /* This next line by Ken Yamaguchi.. ?? */ - outl(0x8, ioaddr + 0x1c); + outl(0x4000, ioaddr + GENCTL); + /* This next magic! line by Ken Yamaguchi.. ?? */ + outl(0x0008, ioaddr + TEST1); /* Pull the chip out of low-power mode, enable interrupts, and set for - PCI read multiple. */ + PCI read multiple. The MIIcfg setting and strange write order are + required by the details of which bits are reset and the transceiver + wiring on the Ositech CardBus card. + */ + outl(0x12, ioaddr + MIICfg); + if (ep->chip_id == 6) + outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); + +#if defined(__powerpc__) || defined(__sparc__) /* Big endian */ + outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL); +#else outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL); +#endif for (i = 0; i < 3; i++) outl(((u16*)dev->dev_addr)[i], ioaddr + LAN0 + i*4); outl(TX_FIFO_THRESH, ioaddr + TxThresh); - full_duplex = tp->full_duplex; - mii_reg5 = mii_read(ioaddr, tp->phys[0], 5); - if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) { - full_duplex = 1; + mii_reg5 = mdio_read(ioaddr, ep->phys[0], 5); + if (mii_reg5 != 0xffff) { + if ((mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040) + ep->full_duplex = 1; + else if (! (mii_reg5 & 0x4000)) + mdio_write(ioaddr, ep->phys[0], 0, 0x1200); if (epic_debug > 1) - printk("%s: Setting %s-duplex based on MII xcvr %d" + printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d" " register read of %4.4x.\n", dev->name, - full_duplex ? "full" : "half", tp->phys[0], - mii_read(ioaddr, tp->phys[0], 5)); + ep->full_duplex ? "full" : "half", ep->phys[0], mii_reg5); } - outl(full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); - outl(virt_to_bus(tp->rx_ring), ioaddr + PRxCDAR); - outl(virt_to_bus(tp->tx_ring), ioaddr + PTxCDAR); + outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); + outl(virt_to_bus(ep->rx_ring), ioaddr + PRxCDAR); + outl(virt_to_bus(ep->tx_ring), ioaddr + PTxCDAR); /* Start the chip's Rx process. */ set_rx_mode(dev); @@ -556,67 +668,160 @@ dev->start = 1; /* Enable interrupts by setting the interrupt mask. */ - outl(CntFull | TxUnderrun | TxDone + outl((ep->chip_id == 6 ? PCIBusErr175 : PCIBusErr170) + | CntFull | TxUnderrun | TxDone | RxError | RxOverflow | RxFull | RxHeader | RxDone, ioaddr + INTMASK); if (epic_debug > 1) - printk("%s: epic_open() ioaddr %4.4x IRQ %d status %4.4x %s-duplex.\n", + printk(KERN_DEBUG "%s: epic_open() ioaddr %lx IRQ %d status %4.4x " + "%s-duplex.\n", dev->name, ioaddr, dev->irq, inl(ioaddr + GENCTL), - full_duplex ? "full" : "half"); + ep->full_duplex ? "full" : "half"); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ - init_timer(&tp->timer); - tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ - tp->timer.data = (unsigned long)dev; - tp->timer.function = &epic_timer; /* timer handler */ - add_timer(&tp->timer); + init_timer(&ep->timer); + ep->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + ep->timer.data = (unsigned long)dev; + ep->timer.function = &epic_timer; /* timer handler */ + add_timer(&ep->timer); return 0; } +/* Reset the chip to recover from a PCI transaction error. + This may occur at interrupt time. */ +static void epic_pause(struct device *dev) +{ + long ioaddr = dev->base_addr; + struct epic_private *ep = (struct epic_private *)dev->priv; + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0x00000000, ioaddr + INTMASK); + /* Stop the chip's Tx and Rx DMA processes. */ + outw(0x0061, ioaddr + COMMAND); + + /* Update the error counts. */ + if (inw(ioaddr + COMMAND) != 0xffff) { + ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); + ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); + ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); + } + + /* Remove the packets on the Rx queue. */ + epic_rx(dev); +} + +static void epic_restart(struct device *dev) +{ + long ioaddr = dev->base_addr; + struct epic_private *ep = (struct epic_private *)dev->priv; + int i; + + printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n", + dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx); + /* Soft reset the chip. */ + outl(0x0001, ioaddr + GENCTL); + + udelay(1); + /* Duplicate code from epic_open(). */ + outl(0x0008, ioaddr + TEST1); + +#if defined(__powerpc__) /* Big endian */ + outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL); +#else + outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL); +#endif + outl(0x12, ioaddr + MIICfg); + if (ep->chip_id == 6) + outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); + + for (i = 0; i < 3; i++) + outl(((u16*)dev->dev_addr)[i], ioaddr + LAN0 + i*4); + + outl(TX_FIFO_THRESH, ioaddr + TxThresh); + outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); + outl(virt_to_bus(&ep->rx_ring[ep->cur_rx%RX_RING_SIZE]), ioaddr + PRxCDAR); + outl(virt_to_bus(&ep->tx_ring[ep->dirty_tx%TX_RING_SIZE]), + ioaddr + PTxCDAR); + + /* Start the chip's Rx process. */ + set_rx_mode(dev); + outl(0x000A, ioaddr + COMMAND); + + /* Enable interrupts by setting the interrupt mask. */ + outl((ep->chip_id == 6 ? PCIBusErr175 : PCIBusErr170) + | CntFull | TxUnderrun | TxDone + | RxError | RxOverflow | RxFull | RxHeader | RxDone, + ioaddr + INTMASK); + printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x" + " interrupt %4.4x.\n", + dev->name, inl(ioaddr + COMMAND), inl(ioaddr + GENCTL), + inl(ioaddr + INTSTAT)); + return; +} + static void epic_timer(unsigned long data) { struct device *dev = (struct device *)data; - struct epic_private *tp = (struct epic_private *)dev->priv; - int ioaddr = dev->base_addr; + struct epic_private *ep = (struct epic_private *)dev->priv; + long ioaddr = dev->base_addr; int next_tick = 0; + int mii_reg5 = mdio_read(ioaddr, ep->phys[0], 5); if (epic_debug > 3) { - printk("%s: Media selection tick, Tx status %8.8x.\n", + printk(KERN_DEBUG "%s: Media selection tick, Tx status %8.8x.\n", dev->name, inl(ioaddr + TxSTAT)); - printk("%s: Other registers are IntMask %4.4x IntStatus %4.4x RxStatus" - " %4.4x.\n", + printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x " + "IntStatus %4.4x RxStatus %4.4x.\n", dev->name, inl(ioaddr + INTMASK), inl(ioaddr + INTSTAT), inl(ioaddr + RxSTAT)); } + if (! ep->force_fd && mii_reg5 != 0xffff) { + int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040; + if (ep->full_duplex != duplex) { + ep->full_duplex = duplex; + printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" + " partner capability of %4.4x.\n", dev->name, + ep->full_duplex ? "full" : "half", ep->phys[0], mii_reg5); + outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); + } + next_tick = 60*HZ; + } if (next_tick) { - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); + ep->timer.expires = RUN_AT(next_tick); + add_timer(&ep->timer); } } static void epic_tx_timeout(struct device *dev) { - struct epic_private *tp = (struct epic_private *)dev->priv; - int ioaddr = dev->base_addr; + struct epic_private *ep = (struct epic_private *)dev->priv; + long ioaddr = dev->base_addr; if (epic_debug > 0) { - printk("%s: Transmit timeout using MII device, Tx status %4.4x.\n", + printk(KERN_WARNING "%s: Transmit timeout using MII device, " + "Tx status %4.4x.\n", dev->name, inw(ioaddr + TxSTAT)); if (epic_debug > 1) { - printk("%s: Tx indices: dirty_tx %d, cur_tx %d.\n", - dev->name, tp->dirty_tx, tp->cur_tx); + printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n", + dev->name, ep->dirty_tx, ep->cur_tx); } } + if (inw(ioaddr + TxSTAT) & 0x10) { /* Tx FIFO underflow. */ + ep->stats.tx_fifo_errors++; + /* Restart the transmit process. */ + outl(0x0080, ioaddr + COMMAND); + } + /* Perhaps stop and restart the chip's Tx processes . */ /* Trigger a transmit demand. */ outl(0x0004, dev->base_addr + COMMAND); dev->trans_start = jiffies; - tp->stats.tx_errors++; + ep->stats.tx_errors++; return; } @@ -624,48 +829,48 @@ static void epic_init_ring(struct device *dev) { - struct epic_private *tp = (struct epic_private *)dev->priv; + struct epic_private *ep = (struct epic_private *)dev->priv; int i; - tp->tx_full = 0; - tp->cur_rx = tp->cur_tx = 0; - tp->dirty_rx = tp->dirty_tx = 0; + ep->tx_full = 0; + ep->cur_rx = ep->cur_tx = 0; + ep->dirty_rx = ep->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { - tp->rx_ring[i].status = 0x8000; /* Owned by Epic chip */ - tp->rx_ring[i].buflength = PKT_BUF_SZ; + ep->rx_ring[i].status = 0x8000; /* Owned by Epic chip */ + ep->rx_ring[i].buflength = PKT_BUF_SZ; { /* Note the receive buffer must be longword aligned. dev_alloc_skb() provides 16 byte alignment. But do *not* use skb_reserve() to align the IP header! */ struct sk_buff *skb; skb = dev_alloc_skb(PKT_BUF_SZ); - tp->rx_skbuff[i] = skb; + ep->rx_skbuff[i] = skb; if (skb == NULL) break; /* Bad news! */ skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - tp->rx_ring[i].bufaddr = virt_to_bus(skb->tail); + ep->rx_ring[i].bufaddr = virt_to_bus(skb->tail); } - tp->rx_ring[i].next = virt_to_bus(&tp->rx_ring[i+1]); + ep->rx_ring[i].next = virt_to_bus(&ep->rx_ring[i+1]); } /* Mark the last entry as wrapping the ring. */ - tp->rx_ring[i-1].next = virt_to_bus(&tp->rx_ring[0]); + ep->rx_ring[i-1].next = virt_to_bus(&ep->rx_ring[0]); /* The Tx buffer descriptor is filled in as needed, but we do need to clear the ownership bit. */ for (i = 0; i < TX_RING_SIZE; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_ring[i].status = 0x0000; - tp->tx_ring[i].next = virt_to_bus(&tp->tx_ring[i+1]); + ep->tx_skbuff[i] = 0; + ep->tx_ring[i].status = 0x0000; + ep->tx_ring[i].next = virt_to_bus(&ep->tx_ring[i+1]); } - tp->tx_ring[i-1].next = virt_to_bus(&tp->tx_ring[0]); + ep->tx_ring[i-1].next = virt_to_bus(&ep->tx_ring[0]); } static int epic_start_xmit(struct sk_buff *skb, struct device *dev) { - struct epic_private *tp = (struct epic_private *)dev->priv; + struct epic_private *ep = (struct epic_private *)dev->priv; int entry; u32 flag; @@ -682,37 +887,37 @@ with the "ownership" bits last. */ /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % TX_RING_SIZE; + entry = ep->cur_tx % TX_RING_SIZE; - tp->tx_skbuff[entry] = skb; - tp->tx_ring[entry].txlength = (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN); - tp->tx_ring[entry].bufaddr = virt_to_bus(skb->data); - tp->tx_ring[entry].buflength = skb->len; + ep->tx_skbuff[entry] = skb; + ep->tx_ring[entry].txlength = (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN); + ep->tx_ring[entry].bufaddr = virt_to_bus(skb->data); + ep->tx_ring[entry].buflength = skb->len; - if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + if (ep->cur_tx - ep->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ flag = 0x10; /* No interrupt */ clear_bit(0, (void*)&dev->tbusy); - } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + } else if (ep->cur_tx - ep->dirty_tx == TX_RING_SIZE/2) { flag = 0x14; /* Tx-done intr. */ clear_bit(0, (void*)&dev->tbusy); - } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + } else if (ep->cur_tx - ep->dirty_tx < TX_RING_SIZE - 2) { flag = 0x10; /* No Tx-done intr. */ clear_bit(0, (void*)&dev->tbusy); } else { /* Leave room for two additional entries. */ flag = 0x14; /* Tx-done intr. */ - tp->tx_full = 1; + ep->tx_full = 1; } - tp->tx_ring[entry].control = flag; - tp->tx_ring[entry].status = 0x8000; /* Pass ownership to the chip. */ - tp->cur_tx++; + ep->tx_ring[entry].control = flag; + ep->tx_ring[entry].status = 0x8000; /* Pass ownership to the chip. */ + ep->cur_tx++; /* Trigger an immediate transmit demand. */ outl(0x0004, dev->base_addr + COMMAND); dev->trans_start = jiffies; if (epic_debug > 4) - printk("%s: Queued Tx packet size %d to slot %d, " + printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, " "flag %2.2x Tx status %8.8x.\n", dev->name, (int)skb->len, entry, flag, inl(dev->base_addr + TxSTAT)); @@ -725,16 +930,27 @@ static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct device *dev = (struct device *)dev_instance; - struct epic_private *lp; + struct epic_private *ep; int status, ioaddr, boguscnt = max_interrupt_work; ioaddr = dev->base_addr; - lp = (struct epic_private *)dev->priv; + ep = (struct epic_private *)dev->priv; + +#if defined(__i386__) + /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", + dev->name); + dev->interrupt = 0; /* Avoid halting machine. */ + return; + } +#else if (dev->interrupt) { - printk("%s: Re-entering the interrupt handler.\n", dev->name); + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } dev->interrupt = 1; +#endif do { status = inl(ioaddr + INTSTAT); @@ -745,18 +961,18 @@ printk("%s: interrupt interrupt=%#8.8x new intstat=%#8.8x.\n", dev->name, status, inl(ioaddr + INTSTAT)); - if ((status & (RxDone | TxEmpty | TxDone)) == 0) + if ((status & IntrSummary) == 0) break; - if (status & RxDone) /* Rx interrupt */ + if (status & (RxDone | RxStarted | RxEarlyWarn)) epic_rx(dev); if (status & (TxEmpty | TxDone)) { int dirty_tx; - for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) { + for (dirty_tx = ep->dirty_tx; dirty_tx < ep->cur_tx; dirty_tx++) { int entry = dirty_tx % TX_RING_SIZE; - int txstatus = lp->tx_ring[entry].status; + int txstatus = ep->tx_ring[entry].status; if (txstatus < 0) break; /* It still hasn't been Txed */ @@ -768,66 +984,77 @@ printk("%s: Transmit error, Tx status %8.8x.\n", dev->name, txstatus); #endif - lp->stats.tx_errors++; - if (txstatus & 0x1050) lp->stats.tx_aborted_errors++; - if (txstatus & 0x0008) lp->stats.tx_carrier_errors++; - if (txstatus & 0x0040) lp->stats.tx_window_errors++; - if (txstatus & 0x0010) lp->stats.tx_fifo_errors++; + ep->stats.tx_errors++; + if (txstatus & 0x1050) ep->stats.tx_aborted_errors++; + if (txstatus & 0x0008) ep->stats.tx_carrier_errors++; + if (txstatus & 0x0040) ep->stats.tx_window_errors++; + if (txstatus & 0x0010) ep->stats.tx_fifo_errors++; #ifdef ETHER_STATS - if (txstatus & 0x1000) lp->stats.collisions16++; + if (txstatus & 0x1000) ep->stats.collisions16++; #endif } else { #ifdef ETHER_STATS - if ((txstatus & 0x0002) != 0) lp->stats.tx_deferred++; + if ((txstatus & 0x0002) != 0) ep->stats.tx_deferred++; #endif - lp->stats.collisions += (txstatus >> 8) & 15; - lp->stats.tx_packets++; + ep->stats.collisions += (txstatus >> 8) & 15; + ep->stats.tx_packets++; } /* Free the original skb. */ - dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); - lp->tx_skbuff[entry] = 0; + DEV_FREE_SKB(ep->tx_skbuff[entry]); + ep->tx_skbuff[entry] = 0; } #ifndef final_version - if (lp->cur_tx - dirty_tx > TX_RING_SIZE) { + if (ep->cur_tx - dirty_tx > TX_RING_SIZE) { printk("%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, lp->cur_tx, lp->tx_full); + dev->name, dirty_tx, ep->cur_tx, ep->tx_full); dirty_tx += TX_RING_SIZE; } #endif - if (lp->tx_full && dev->tbusy - && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { + if (ep->tx_full && dev->tbusy + && dirty_tx > ep->cur_tx - TX_RING_SIZE + 2) { /* The ring is no longer full, clear tbusy. */ - lp->tx_full = 0; + ep->tx_full = 0; clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } - lp->dirty_tx = dirty_tx; + ep->dirty_tx = dirty_tx; } /* Check uncommon events all at once. */ - if (status & (CntFull | TxUnderrun | RxOverflow)) { + if (status & (CntFull | TxUnderrun | RxOverflow | + PCIBusErr170 | PCIBusErr175)) { + if (status == 0xffffffff) /* Chip failed or removed (CardBus). */ + break; /* Always update the error counts to avoid overhead later. */ - lp->stats.rx_missed_errors += inb(ioaddr + MPCNT); - lp->stats.rx_frame_errors += inb(ioaddr + ALICNT); - lp->stats.rx_crc_errors += inb(ioaddr + CRCCNT); + ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); + ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); + ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); if (status & TxUnderrun) { /* Tx FIFO underflow. */ - lp->stats.tx_fifo_errors++; - /* Restart the transmit process. */ - outl(0x0080, ioaddr + COMMAND); + ep->stats.tx_fifo_errors++; + outl(1536, ioaddr + TxThresh); + /* Restart the transmit process. */ + outl(0x0080, ioaddr + COMMAND); } if (status & RxOverflow) { /* Missed a Rx frame. */ - lp->stats.rx_errors++; + ep->stats.rx_errors++; + } + if (status & PCIBusErr170) { + printk(KERN_ERR "%s: PCI Bus Error! EPIC status %4.4x.\n", + dev->name, status); + epic_pause(dev); + epic_restart(dev); } /* Clear all error sources. */ outl(status & 0x7f18, ioaddr + INTSTAT); } if (--boguscnt < 0) { - printk("%s: Too much work at interrupt, IntrStatus=0x%8.8x.\n", + printk(KERN_ERR "%s: Too much work at interrupt, " + "IntrStatus=0x%8.8x.\n", dev->name, status); /* Clear all interrupt sources. */ outl(0x0001ffff, ioaddr + INTSTAT); @@ -836,115 +1063,101 @@ } while (1); if (epic_debug > 3) - printk("%s: exiting interrupt, intr_status=%#4.4x.\n", + printk(KERN_DEBUG "%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, inl(ioaddr + INTSTAT)); +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else dev->interrupt = 0; +#endif return; } -static int -epic_rx(struct device *dev) +static int epic_rx(struct device *dev) { - struct epic_private *lp = (struct epic_private *)dev->priv; - int entry = lp->cur_rx % RX_RING_SIZE; + struct epic_private *ep = (struct epic_private *)dev->priv; + int entry = ep->cur_rx % RX_RING_SIZE; + int work_done = 0; if (epic_debug > 4) - printk(" In epic_rx(), entry %d %8.8x.\n", entry, - lp->rx_ring[entry].status); + printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry, + ep->rx_ring[entry].status); /* If we own the next entry, it's a new packet. Send it up. */ - while (lp->rx_ring[entry].status >= 0) { - int status = lp->rx_ring[entry].status; + while (ep->rx_ring[entry].status >= 0 && ep->rx_skbuff[entry]) { + int status = ep->rx_ring[entry].status; if (epic_debug > 4) - printk(" epic_rx() status was %8.8x.\n", status); - if (status & 0x2000) { - printk("%s: Oversized Ethernet frame spanned multiple buffers," - " status %4.4x!\n", dev->name, status); - lp->stats.rx_length_errors++; - } else if (status & 0x0006) { - /* Rx Frame errors are counted in hardware. */ - lp->stats.rx_errors++; + printk(KERN_DEBUG " epic_rx() status was %8.8x.\n", status); + if (status & 0x2006) { + if (status & 0x2000) { + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " + "multiple buffers, status %4.4x!\n", dev->name, status); + ep->stats.rx_length_errors++; + } else if (status & 0x0006) + /* Rx Frame errors are counted in hardware. */ + ep->stats.rx_errors++; } else { /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ - short pkt_len = lp->rx_ring[entry].rxlength - 4; + short pkt_len = ep->rx_ring[entry].rxlength - 4; struct sk_buff *skb; - int rx_in_place = 0; - /* Check if the packet is long enough to just accept without - copying to a properly sized skbuff. */ - if (pkt_len > rx_copybreak) { - struct sk_buff *newskb; - char *temp; - - /* Pass up the skb already on the Rx ring. */ - skb = lp->rx_skbuff[entry]; - temp = skb_put(skb, pkt_len); - if (bus_to_virt(lp->rx_ring[entry].bufaddr) != temp) - printk("%s: Warning -- the skbuff addresses do not match" - " in epic_rx: %p vs. %p / %p.\n", dev->name, - bus_to_virt(lp->rx_ring[entry].bufaddr), - skb->head, temp); - /* Get a fresh skbuff to replace the filled one. */ - newskb = dev_alloc_skb(PKT_BUF_SZ); - if (newskb) { - rx_in_place = 1; - lp->rx_skbuff[entry] = newskb; - newskb->dev = dev; - /* Align IP on 16 byte boundaries */ - skb_reserve(newskb, 2); - lp->rx_ring[entry].bufaddr = virt_to_bus(newskb->tail); - } else /* No memory, drop the packet. */ - skb = 0; - } else - skb = dev_alloc_skb(pkt_len + 2); - if (skb == NULL) { - int i; - printk("%s: Memory squeeze, deferring packet.\n", dev->name); - /* Check that at least two ring entries are free. - If not, free one and mark stats->rx_dropped++. */ - for (i = 0; i < RX_RING_SIZE; i++) - if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0) - break; - - if (i > RX_RING_SIZE -2) { - lp->stats.rx_dropped++; - lp->rx_ring[entry].status = 0x8000; - lp->cur_rx++; - } - break; - } - skb->dev = dev; - if (! rx_in_place) { - skb_reserve(skb, 2); /* 16 byte align the data fields */ + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if 1 /* USE_IP_COPYSUM */ + eth_copy_and_sum(skb, bus_to_virt(ep->rx_ring[entry].bufaddr), + pkt_len, 0); + skb_put(skb, pkt_len); +#else memcpy(skb_put(skb, pkt_len), - bus_to_virt(lp->rx_ring[entry].bufaddr), pkt_len); + bus_to_virt(ep->rx_ring[entry].bufaddr), pkt_len); +#endif + } else { + skb_put(skb = ep->rx_skbuff[entry], pkt_len); + ep->rx_skbuff[entry] = NULL; } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); - lp->stats.rx_packets++; + ep->stats.rx_packets++; } - - lp->rx_ring[entry].status = 0x8000; - entry = (++lp->cur_rx) % RX_RING_SIZE; + work_done++; + entry = (++ep->cur_rx) % RX_RING_SIZE; } - return 0; + /* Refill the Rx ring buffers. */ + for (; ep->cur_rx - ep->dirty_rx > 0; ep->dirty_rx++) { + entry = ep->dirty_rx % RX_RING_SIZE; + if (ep->rx_skbuff[entry] == NULL) { + struct sk_buff *skb; + skb = ep->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + ep->rx_ring[entry].bufaddr = virt_to_bus(skb->tail); + work_done++; + } + ep->rx_ring[entry].status = 0x8000; + } + return work_done; } -static int -epic_close(struct device *dev) +static int epic_close(struct device *dev) { - int ioaddr = dev->base_addr; - struct epic_private *tp = (struct epic_private *)dev->priv; + long ioaddr = dev->base_addr; + struct epic_private *ep = (struct epic_private *)dev->priv; int i; dev->start = 0; dev->tbusy = 1; if (epic_debug > 1) - printk("%s: Shutting down ethercard, status was %2.2x.\n", + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", dev->name, inl(ioaddr + INTSTAT)); /* Disable interrupts by clearing the interrupt mask. */ @@ -953,62 +1166,61 @@ outw(0x0061, ioaddr + COMMAND); /* Update the error counts. */ - tp->stats.rx_missed_errors += inb(ioaddr + MPCNT); - tp->stats.rx_frame_errors += inb(ioaddr + ALICNT); - tp->stats.rx_crc_errors += inb(ioaddr + CRCCNT); + ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); + ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); + ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); - del_timer(&tp->timer); + del_timer(&ep->timer); free_irq(dev->irq, dev); /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = tp->rx_skbuff[i]; - tp->rx_skbuff[i] = 0; - tp->rx_ring[i].status = 0; /* Not owned by Epic chip. */ - tp->rx_ring[i].buflength = 0; - tp->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */ + struct sk_buff *skb = ep->rx_skbuff[i]; + ep->rx_skbuff[i] = 0; + ep->rx_ring[i].status = 0; /* Not owned by Epic chip. */ + ep->rx_ring[i].buflength = 0; + ep->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */ if (skb) { #if LINUX_VERSION_CODE < 0x20100 skb->free = 1; #endif - dev_kfree_skb(skb, FREE_WRITE); + DEV_FREE_SKB(skb); } } for (i = 0; i < TX_RING_SIZE; i++) { - if (tp->tx_skbuff[i]) - dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); - tp->tx_skbuff[i] = 0; + if (ep->tx_skbuff[i]) + DEV_FREE_SKB(ep->tx_skbuff[i]); + ep->tx_skbuff[i] = 0; } /* Green! Leave the chip in low-power mode. */ outl(0x0008, ioaddr + GENCTL); - + MOD_DEC_USE_COUNT; return 0; } -static struct enet_statistics * -epic_get_stats(struct device *dev) +static struct net_device_stats *epic_get_stats(struct device *dev) { - struct epic_private *tp = (struct epic_private *)dev->priv; - int ioaddr = dev->base_addr; + struct epic_private *ep = (struct epic_private *)dev->priv; + long ioaddr = dev->base_addr; if (dev->start) { /* Update the error counts. */ - tp->stats.rx_missed_errors += inb(ioaddr + MPCNT); - tp->stats.rx_frame_errors += inb(ioaddr + ALICNT); - tp->stats.rx_crc_errors += inb(ioaddr + CRCCNT); + ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); + ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); + ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); } - return &tp->stats; + return &ep->stats; } /* Set or clear the multicast filter for this adaptor. Note that we only use exclusion around actually queueing the - new frame, not around filling tp->setup_frame. This is non-deterministic + new frame, not around filling ep->setup_frame. This is non-deterministic when re-entered but still correct. */ /* The little-endian AUTODIN II ethernet CRC calculation. @@ -1035,15 +1247,15 @@ static void set_rx_mode(struct device *dev) { - int ioaddr = dev->base_addr; - struct epic_private *tp = (struct epic_private *)dev->priv; + long ioaddr = dev->base_addr; + struct epic_private *ep = (struct epic_private *)dev->priv; unsigned char mc_filter[8]; /* Multicast hash filter */ int i; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ outl(0x002C, ioaddr + RxCtrl); /* Unconditionally log net taps. */ - printk("%s: Promiscuous mode enabled.\n", dev->name); + printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); memset(mc_filter, 0xff, sizeof(mc_filter)); } else if ((dev->mc_count > 0) || (dev->flags & IFF_ALLMULTI)) { /* There is apparently a chip bug, so the multicast filter @@ -1064,14 +1276,56 @@ mc_filter); } /* ToDo: perhaps we need to stop the Tx and Rx process here? */ - if (memcmp(mc_filter, tp->mc_filter, sizeof(mc_filter))) { + if (memcmp(mc_filter, ep->mc_filter, sizeof(mc_filter))) { for (i = 0; i < 4; i++) outw(((u16 *)mc_filter)[i], ioaddr + MC0 + i*4); - memcpy(tp->mc_filter, mc_filter, sizeof(mc_filter)); + memcpy(ep->mc_filter, mc_filter, sizeof(mc_filter)); } return; } +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + long ioaddr = dev->base_addr; + u16 *data = (u16 *)&rq->ifr_data; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = ((struct epic_private *)dev->priv)->phys[0] & 0x1f; + /* Fall Through */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + if (! dev->start) { + outl(0x0200, ioaddr + GENCTL); + outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); + } + data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); + if (! dev->start) { +#ifdef notdef + outl(0x0008, ioaddr + GENCTL); + outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL); +#endif + } + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + if (! dev->start) { + outl(0x0200, ioaddr + GENCTL); + outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); + } + mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); + if (! dev->start) { +#ifdef notdef + outl(0x0008, ioaddr + GENCTL); + outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL); +#endif + } + return 0; + default: + return -EOPNOTSUPP; + } +} + #ifdef CARDBUS @@ -1091,7 +1345,7 @@ pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); io &= ~3; - dev = epic100_probe1(bus, devfn, NULL, -1); + dev = epic_probe1(bus, devfn, NULL, -1); if (dev) { dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); strcpy(node->dev_name, dev->name); @@ -1103,6 +1357,33 @@ return NULL; } +static void epic_suspend(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "epic_suspend(%s)\n", node->dev_name); + for (devp = &root_epic_dev; *devp; devp = next) { + next = &((struct epic_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + long ioaddr = (*devp)->base_addr; + epic_pause(*devp); + /* Put the chip into low-power mode. */ + outl(0x0008, ioaddr + GENCTL); + } +} +static void epic_resume(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "epic_resume(%s)\n", node->dev_name); + for (devp = &root_epic_dev; *devp; devp = next) { + next = &((struct epic_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + epic_restart(*devp); + } +} static void epic_detach(dev_node_t *node) { struct device **devp, **next; @@ -1121,7 +1402,7 @@ } struct driver_operations epic_ops = { - "epic_cb", epic_attach, NULL, NULL, epic_detach + "epic_cb", epic_attach, epic_suspend, epic_resume, epic_detach }; #endif /* Cardbus support */ @@ -1169,9 +1450,8 @@ /* * Local variables: - * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c" - * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c" - * alt-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic.c -o epic_cb.c -I/usr/src/pcmcia-cs-3.0.0/include/" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c -o epic_cb.o -I/usr/src/pcmcia-cs-3.0.5/include/" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff -u --recursive --new-file v2.0.35/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c --- v2.0.35/linux/drivers/net/eth16i.c Sun Nov 15 10:49:38 1998 +++ linux/drivers/net/eth16i.c Sun Nov 15 10:33:03 1998 @@ -1,19 +1,31 @@ /* eth16i.c An ICL EtherTeam 16i and 32 EISA ethernet driver for Linux - - Written 1994-95 by Mika Kuoppala - - Copyright (C) 1994, 1995 by Mika Kuoppala - Based on skeleton.c and at1700.c by Donald Becker + + Written 1994-1998 by Mika Kuoppala + + Copyright (C) 1994-1998 by Mika Kuoppala + Based on skeleton.c and heavily on at1700.c by Donald Becker This software may be used and distributed according to the terms of the GNU Public Licence, incorporated herein by reference. - The author may be reached as miku@elt.icl.fi + The author may be reached as miku@iki.fi This driver supports following cards : - ICL EtherTeam 16i - - ICL EtherTeam 32 EISA + - ICL EtherTeam 32 EISA + (Uses true 32 bit transfers rather than 16i compability mode) + + Example Module usage: + insmod eth16i.o ioaddr=0x2a0 mediatype=bnc + mediatype can be one of the following: bnc,tp,dix,auto,eprom + + 'auto' will try to autoprobe mediatype. + 'eprom' will use whatever type defined in eprom. + + I have benchmarked driver with PII/300Mhz as a ftp client + and 486/33Mhz as a ftp server. Top speed was 1128.37 kilobytes/sec. + Sources: - skeleton.c a sample network driver core for linux, written by Donald Becker @@ -23,61 +35,109 @@ written by Markku Viima - The Fujitsu MB86965 databook. - Valuable assistance from: - Markku Viima (ICL) - Ari Valve (ICL) - + Author thanks following persons due to their valueble assistance: + Markku Viima (ICL) + Ari Valve (ICL) + Donald Becker + Kurt Huwig + Revision history: Version Date Description - 0.01 15.12-94 Initial version (card detection) + 0.01 15.12-94 Initial version (card detection) 0.02 23.01-95 Interrupt is now hooked correctly 0.03 01.02-95 Rewrote initialization part 0.04 07.02-95 Base skeleton done... - Made a few changes to signature checking - to make it a bit reliable. - - fixed bug in tx_buf mapping - - fixed bug in initialization (DLC_EN - wasn't enabled when initialization - was done.) - 0.05 08.02-95 If there were more than one packet to send, - transmit was jammed due to invalid - register write...now fixed + Made a few changes to signature checking + to make it a bit reliable. + - fixed bug in tx_buf mapping + - fixed bug in initialization (DLC_EN + wasn't enabled when initialization + was done.) + 0.05 08.02-95 If there were more than one packet to send, + transmit was jammed due to invalid + register write...now fixed 0.06 19.02-95 Rewrote interrupt handling 0.07 13.04-95 Wrote EEPROM read routines Card configuration now set according to - data read from EEPROM + data read from EEPROM 0.08 23.06-95 Wrote part that tries to probe used interface port if AUTO is selected - 0.09 01.09-95 Added module support - - 0.10 04.09-95 Fixed receive packet allocation to work - with kernels > 1.3.x + 0.09 01.09-95 Added module support + 0.10 04.09-95 Fixed receive packet allocation to work + with kernels > 1.3.x + 0.20 20.09-95 Added support for EtherTeam32 EISA 0.21 17.10-95 Removed the unnecessary extern init_etherdev() declaration. Some other cleanups. + + 0.22 22.02-96 Receive buffer was not flushed + correctly when faulty packet was + received. Now fixed. + + 0.23 26.02-96 Made resetting the adapter + more reliable. + + 0.24 27.02-96 Rewrote faulty packet handling in eth16i_rx + + 0.25 22.05-96 kfree() was missing from cleanup_module. + + 0.26 11.06-96 Sometimes card was not found by + check_signature(). Now made more reliable. + + 0.27 23.06-96 Oops. 16 consecutive collisions halted + adapter. Now will try to retransmit + MAX_COL_16 times before finally giving up. + + 0.28 28.10-97 Added dev_id parameter (NULL) for free_irq + + 0.29 29.10-97 Multiple card support for module users + + 0.30 30.10-97 Fixed irq allocation bug. + (request_irq moved from probe to open) - 0.21a 15.08-97 Made signature check less restrictive to - detect card that have been used for booting - with a bootprom. + 0.30a 21.08-98 Card detection made more relaxed. Driver + had problems with some TCP/IP-PROM boots + to find the card. Suggested by Kurt Huwig + + 0.31 28.08-98 Media interface port can now be selected + with module parameters or kernel + boot parameters. + + 0.32 31.08-98 IRQ was never freed if open/close + pair wasn't called. Now fixed. + + 0.33 10.09-98 When eth16i_open() was called after + eth16i_close() chip never recovered. + Now more shallow reset is made on + close. + Bugs: - In some cases the interface autoprobing code doesn't find + In some cases the media interface autoprobing code doesn't find the correct interface type. In this case you can manually choose the interface type in DOS with E16IC.EXE which is configuration software for EtherTeam16i and EtherTeam32 cards. - + This is also true for IRQ setting. You cannot use module + parameter to configure IRQ of the card (yet). + To do: - Real multicast support + - Rewrite the media interface autoprobing code. Its _horrible_ ! + - Possibly merge all the MB86965 specific code to external + module for use by eth16.c and Donald's at1700.c + - IRQ configuration with module parameter. I will do + this when i will get enough info about setting + irq without configuration utility. */ static char *version = - "eth16i.c: v0.21a 15-08-97 Mika Kuoppala (miku@elt.icl.fi)/Kurt Huwig (kurt@huwig.de)\n"; + "eth16i.c: v0.33 10-09-98 Mika Kuoppala (miku@iki.fi)\n"; #include @@ -102,22 +162,44 @@ #include #include +#ifndef LINUX_VERSION_CODE +#include +#endif + +#if LINUX_VERSION_CODE >= 0x20123 +#include +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +#if LINUX_VERSION_CODE < 0x20138 +#define test_and_set_bit(val,addr) set_bit(val,addr) +#endif + +#if LINUX_VERSION_CODE < 0x020100 +typedef struct enet_statistics eth16i_stats_type; +#else +typedef struct net_device_stats eth16i_stats_type; +#endif + /* Few macros */ -#define BIT(a) ( (1 << (a)) ) +#define BIT(a) ( (1 << (a)) ) #define BITSET(ioaddr, bnum) ((outb(((inb(ioaddr)) | (bnum)), ioaddr))) #define BITCLR(ioaddr, bnum) ((outb(((inb(ioaddr)) & (~(bnum))), ioaddr))) /* This is the I/O address space for Etherteam 16i adapter. */ -#define ETH16I_IO_EXTENT 32 +#define ETH16I_IO_EXTENT 32 /* Ticks before deciding that transmit has timed out */ -#define TIMEOUT_TICKS 30 +#define TX_TIMEOUT (400*HZ/1000) /* Maximum loop count when receiving packets */ -#define MAX_RX_LOOP 40 +#define MAX_RX_LOOP 20 /* Some interrupt masks */ -#define ETH16I_INTR_ON 0x8f82 +#define ETH16I_INTR_ON 0xef8a /* Higher is receive mask */ #define ETH16I_INTR_OFF 0x0000 /* Buffers header status byte meanings */ @@ -134,6 +216,7 @@ #define NET_BUSY BIT(6) #define TX_PKT_RCD BIT(5) #define CR_LOST BIT(4) +#define TX_JABBER_ERR BIT(3) #define COLLISION BIT(2) #define COLLISIONS_16 BIT(1) @@ -186,16 +269,16 @@ #define BUFFER_WIDTH_8 BIT(4) /* 1 = 8bit, 0 = 16bit */ #define TBS1 BIT(3) #define TBS0 BIT(2) -#define _BS1 BIT(1) /* 00=8kb, 01=16kb */ -#define _BS0 BIT(0) /* 10=32kb, 11=64kb */ +#define SRAM_BS1 BIT(1) /* 00=8kb, 01=16kb */ +#define SRAM_BS0 BIT(0) /* 10=32kb, 11=64kb */ #ifndef ETH16I_TX_BUF_SIZE /* 0 = 2kb, 1 = 4kb */ -#define ETH16I_TX_BUF_SIZE 2 /* 2 = 8kb, 3 = 16kb */ +#define ETH16I_TX_BUF_SIZE 3 /* 2 = 8kb, 3 = 16kb */ #endif -#define TX_BUF_1x2048 0 -#define TX_BUF_2x2048 1 -#define TX_BUF_2x4098 2 -#define TX_BUF_2x8192 3 +#define TX_BUF_1x2048 0 +#define TX_BUF_2x2048 1 +#define TX_BUF_2x4098 2 +#define TX_BUF_2x8192 3 /* Configuration Register 1 (DLCR7) */ #define CONFIG_REG_1 7 @@ -215,15 +298,18 @@ #define HASH_TABLE_RB 1 /* Buffer memory ports */ -#define BUFFER_MEM_PORT_LB 8 -#define DATAPORT BUFFER_MEM_PORT_LB -#define BUFFER_MEM_PORT_HB 9 +#define BUFFER_MEM_PORT_LB 8 +#define DATAPORT BUFFER_MEM_PORT_LB +#define BUFFER_MEM_PORT_HB 9 /* 16 Collision control register (BMPR11) */ #define COL_16_REG 11 #define HALT_ON_16 0x00 #define RETRANS_AND_HALT_ON_16 0x02 +/* Maximum number of attempts to send after 16 concecutive collisions */ +#define MAX_COL_16 10 + /* DMA Burst and Transceiver Mode Register (BMPR13) */ #define TRANSCEIVER_MODE_REG 13 #define TRANSCEIVER_MODE_RB 2 @@ -235,9 +321,8 @@ /* Filter Self Receive Register (BMPR14) */ #define FILTER_SELF_RX_REG 14 -#define SKIP_RECEIVE_PACKET BIT(2) +#define SKIP_RX_PACKET BIT(2) #define FILTER_SELF_RECEIVE BIT(0) -#define RX_BUF_SKIP_PACKET SKIP_RECEIVE_PACKET | FILTER_SELF_RECEIVE /* EEPROM Control Register (BMPR 16) */ #define EEPROM_CTRL_REG 16 @@ -257,19 +342,20 @@ #define EEPROM_READ 0x80 /* NMC93CSx6 EEPROM Addresses */ -#define E_NODEID_0 0x02 -#define E_NODEID_1 0x03 -#define E_NODEID_2 0x04 -#define E_PORT_SELECT 0x14 - #define E_PORT_BNC 0 - #define E_PORT_DIX 1 - #define E_PORT_TP 2 - #define E_PORT_AUTO 3 -#define E_PRODUCT_CFG 0x30 +#define E_NODEID_0 0x02 +#define E_NODEID_1 0x03 +#define E_NODEID_2 0x04 +#define E_PORT_SELECT 0x14 + #define E_PORT_BNC 0x00 + #define E_PORT_DIX 0x01 + #define E_PORT_TP 0x02 + #define E_PORT_AUTO 0x03 + #define E_PORT_FROM_EPROM 0x04 +#define E_PRODUCT_CFG 0x30 /* Macro to slow down io between EEPROM clock transitions */ -#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { __SLOW_DOWN_IO; }}while(0) +#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { inb(0x80); }}while(0) /* Jumperless Configuration Register (BMPR19) */ #define JUMPERLESS_CONFIG 19 @@ -288,14 +374,16 @@ 0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0 }; /* This is the Interrupt lookup table for Eth16i card */ -static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15 }; +static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15, 0 }; +#define NUM_OF_ISA_IRQS 4 /* This is the Interrupt lookup table for Eth32i card */ -static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15 }; +static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15, 0 }; #define EISA_IRQ_REG 0xc89 +#define NUM_OF_EISA_IRQS 8 static unsigned int eth16i_tx_buf_map[] = { 2048, 2048, 4096, 8192 }; -unsigned int boot = 1; +static unsigned int boot = 1; /* Use 0 for production, 1 for verification, >2 for debug */ #ifndef ETH16I_DEBUG @@ -304,916 +392,1213 @@ static unsigned int eth16i_debug = ETH16I_DEBUG; /* Information for each board */ + struct eth16i_local { - struct enet_statistics stats; - unsigned int tx_started:1; - unsigned char tx_queue; /* Number of packets in transmit buffer */ - unsigned short tx_queue_len; - unsigned int tx_buf_size; - unsigned long open_time; + eth16i_stats_type stats; + unsigned char tx_started; + unsigned char tx_buf_busy; + unsigned short tx_queue; /* Number of packets in transmit buffer */ + unsigned short tx_queue_len; + unsigned int tx_buf_size; + unsigned long open_time; + unsigned long tx_buffered_packets; + unsigned long col_16; }; /* Function prototypes */ -extern int eth16i_probe(struct device *dev); +extern int eth16i_probe(struct device *dev); + +static int eth16i_probe1(struct device *dev, int ioaddr); +static int eth16i_check_signature(int ioaddr); +static int eth16i_probe_port(int ioaddr); +static void eth16i_set_port(int ioaddr, int porttype); +static int eth16i_send_probe_packet(int ioaddr, unsigned char *b, int l); +static int eth16i_receive_probe_packet(int ioaddr); +static int eth16i_get_irq(int ioaddr); +static int eth16i_read_eeprom(int ioaddr, int offset); +static int eth16i_read_eeprom_word(int ioaddr); +static void eth16i_eeprom_cmd(int ioaddr, unsigned char command); +static int eth16i_open(struct device *dev); +static int eth16i_close(struct device *dev); +static int eth16i_tx(struct sk_buff *skb, struct device *dev); +static void eth16i_rx(struct device *dev); +static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void eth16i_reset(struct device *dev); +static void eth16i_skip_packet(struct device *dev); +static void eth16i_multicast(struct device *dev); +static void eth16i_select_regbank(unsigned char regbank, int ioaddr); +static void eth16i_initialize(struct device *dev); + +#if 0 +static int eth16i_set_irq(struct device *dev); +#endif + +#ifdef MODULE +static ushort eth16i_parse_mediatype(const char* s); +#endif -static int eth16i_probe1(struct device *dev, short ioaddr); -static int eth16i_check_signature(short ioaddr); -static int eth16i_probe_port(short ioaddr); -static void eth16i_set_port(short ioaddr, int porttype); -static int eth16i_send_probe_packet(short ioaddr, unsigned char *b, int l); -static int eth16i_receive_probe_packet(short ioaddr); -static int eth16i_get_irq(short ioaddr); -static int eth16i_read_eeprom(int ioaddr, int offset); -static int eth16i_read_eeprom_word(int ioaddr); -static void eth16i_eeprom_cmd(int ioaddr, unsigned char command); -static int eth16i_open(struct device *dev); -static int eth16i_close(struct device *dev); -static int eth16i_tx(struct sk_buff *skb, struct device *dev); -static void eth16i_rx(struct device *dev); -static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void eth16i_multicast(struct device *dev); -static void eth16i_select_regbank(unsigned char regbank, short ioaddr); -static void eth16i_initialize(struct device *dev); static struct enet_statistics *eth16i_get_stats(struct device *dev); static char *cardname = "ICL EtherTeam 16i/32"; #ifdef HAVE_DEVLIST + /* Support for alternate probe manager */ /struct netdev_entry eth16i_drv = - {"eth16i", eth16i_probe1, ETH16I_IO_EXTENT, eth16i_probe_list}; + {"eth16i", eth16i_probe1, ETH16I_IO_EXTENT, eth16i_probe_list}; #else /* Not HAVE_DEVLIST */ -int eth16i_probe(struct device *dev) + +__initfunc(int eth16i_probe(struct device *dev)) { - int i; - int ioaddr; - int base_addr = dev ? dev->base_addr : 0; - - if(eth16i_debug > 4) - printk("Probing started for %s\n", cardname); - - if(base_addr > 0x1ff) /* Check only single location */ - return eth16i_probe1(dev, base_addr); - else if(base_addr != 0) /* Don't probe at all */ - return ENXIO; - - /* Seek card from the ISA io address space */ - for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++) { - if(check_region(ioaddr, ETH16I_IO_EXTENT)) - continue; - if(eth16i_probe1(dev, ioaddr) == 0) - return 0; - } - - /* Seek card from the EISA io address space */ - for(i = 0; (ioaddr = eth32i_portlist[i]) ; i++) { - if(check_region(ioaddr, ETH16I_IO_EXTENT)) - continue; - if(eth16i_probe1(dev, ioaddr) == 0) - return 0; - } + int i; + int ioaddr; + int base_addr = dev ? dev->base_addr : 0; + + if(eth16i_debug > 4) + printk(KERN_DEBUG "Probing started for %s\n", cardname); + + if(base_addr > 0x1ff) /* Check only single location */ + return eth16i_probe1(dev, base_addr); + else if(base_addr != 0) /* Don't probe at all */ + return ENXIO; + + /* Seek card from the ISA io address space */ + for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++) { + if(check_region(ioaddr, ETH16I_IO_EXTENT)) + continue; + if(eth16i_probe1(dev, ioaddr) == 0) + return 0; + } + + /* Seek card from the EISA io address space */ + for(i = 0; (ioaddr = eth32i_portlist[i]) ; i++) { + if(check_region(ioaddr, ETH16I_IO_EXTENT)) + continue; + if(eth16i_probe1(dev, ioaddr) == 0) + return 0; + } - return ENODEV; + return ENODEV; } #endif /* Not HAVE_DEVLIST */ -static int eth16i_probe1(struct device *dev, short ioaddr) +__initfunc(static int eth16i_probe1(struct device *dev, int ioaddr)) { - static unsigned version_printed = 0; - unsigned int irq = 0; - boot = 1; /* To inform initialization that we are in boot probe */ - - /* - The MB86985 chip has on register which holds information in which - io address the chip lies. First read this register and compare - it to our current io address and if match then this could - be our chip. - */ - - if(ioaddr < 0x1000) { - if(eth16i_portlist[(inb(ioaddr + JUMPERLESS_CONFIG) & 0x07)] != ioaddr) - return -ENODEV; - } + static unsigned version_printed = 0; + boot = 1; /* To inform initilization that we are in boot probe */ - /* Now we will go a bit deeper and try to find the chip's signature */ + /* + The MB86985 chip has on register which holds information in which + io address the chip lies. First read this register and compare + it to our current io address and if match then this could + be our chip. + */ - if(eth16i_check_signature(ioaddr) != 0) /* Can we find the signature here */ - return -ENODEV; - - /* - Now it seems that we have found a ethernet chip in this particular - ioaddr. The MB86985 chip has this feature, that when you read a - certain register it will increase its io base address to next - configurable slot. Now when we have found the chip, first thing is - to make sure that the chip's ioaddr will hold still here. - */ - - eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); - outb(0x00, ioaddr + TRANSCEIVER_MODE_REG); - - outb(0x00, ioaddr + RESET); /* Will reset some parts of chip */ - BITSET(ioaddr + CONFIG_REG_0, BIT(7)); /* This will disable the data link */ - - if(dev == NULL) - dev = init_etherdev(0, sizeof(struct eth16i_local)); + if(ioaddr < 0x1000) { + + if(eth16i_portlist[(inb(ioaddr + JUMPERLESS_CONFIG) & 0x07)] + != ioaddr) + return -ENODEV; + } - if( (eth16i_debug & version_printed++) == 0) - printk(version); + /* Now we will go a bit deeper and try to find the chip's signature */ - dev->base_addr = ioaddr; - - irq = eth16i_get_irq(ioaddr); - dev->irq = irq; + if(eth16i_check_signature(ioaddr) != 0) + return -ENODEV; - /* Try to obtain interrupt vector */ - if(request_irq(dev->irq, ð16i_interrupt, 0, "eth16i", NULL)) { - printk("%s: %s at %#3x, but is unusable due - conflict on IRQ %d.\n", dev->name, cardname, ioaddr, irq); - return EAGAIN; - } - - printk("%s: %s at %#3x, IRQ %d, ", - dev->name, cardname, ioaddr, dev->irq); - - /* Let's grab the region */ - request_region(ioaddr, ETH16I_IO_EXTENT, "eth16i"); - - /* Now we will have to lock the chip's io address */ - eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); - outb(0x38, ioaddr + TRANSCEIVER_MODE_REG); + /* + Now it seems that we have found a ethernet chip in this particular + ioaddr. The MB86985 chip has this feature, that when you read a + certain register it will increase it's io base address to next + configurable slot. Now when we have found the chip, first thing is + to make sure that the chip's ioaddr will hold still here. + */ + + eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); + outb(0x00, ioaddr + TRANSCEIVER_MODE_REG); + + outb(0x00, ioaddr + RESET); /* Reset some parts of chip */ + BITSET(ioaddr + CONFIG_REG_0, BIT(7)); /* Disable the data link */ - eth16i_initialize(dev); /* Initialize rest of the chip's registers */ - - /* Now let's same some energy by shutting down the chip ;) */ - BITCLR(ioaddr + CONFIG_REG_1, POWERUP); - - /* Initialize the device structure */ - if(dev->priv == NULL) - dev->priv = kmalloc(sizeof(struct eth16i_local), GFP_KERNEL); - memset(dev->priv, 0, sizeof(struct eth16i_local)); - - dev->open = eth16i_open; - dev->stop = eth16i_close; - dev->hard_start_xmit = eth16i_tx; - dev->get_stats = eth16i_get_stats; - dev->set_multicast_list = eth16i_multicast; + if(dev == NULL) + dev = init_etherdev(0, 0); + + if( (eth16i_debug & version_printed++) == 0) + printk(KERN_INFO "%s", version); - /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); + dev->base_addr = ioaddr; + +#if 0 + if(dev->irq) { + if(eth16i_set_irq(dev)) { + dev->irq = eth16i_get_irq(ioaddr); + } - boot = 0; + } + else { +#endif - return 0; + dev->irq = eth16i_get_irq(ioaddr); + + /* Try to obtain interrupt vector */ + + if (request_irq(dev->irq, (void *)ð16i_interrupt, 0, "eth16i", dev)) { + printk(KERN_WARNING "%s: %s at %#3x, but is unusable due conflicting IRQ %d.\n", + dev->name, cardname, ioaddr, dev->irq); + return -EAGAIN; + } + +#if 0 + irq2dev_map[dev->irq] = dev; +#endif + + printk(KERN_INFO "%s: %s at %#3x, IRQ %d, ", + dev->name, cardname, ioaddr, dev->irq); + + /* Let's grab the region */ + request_region(ioaddr, ETH16I_IO_EXTENT, "eth16i"); + + /* Now we will have to lock the chip's io address */ + eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); + outb(0x38, ioaddr + TRANSCEIVER_MODE_REG); + + eth16i_initialize(dev); /* Initialize rest of the chip's registers */ + + /* Now let's same some energy by shutting down the chip ;) */ + BITCLR(ioaddr + CONFIG_REG_1, POWERUP); + + /* Initialize the device structure */ + if(dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct eth16i_local), GFP_KERNEL); + if(dev->priv == NULL) + return -ENOMEM; + } + + memset(dev->priv, 0, sizeof(struct eth16i_local)); + + dev->open = eth16i_open; + dev->stop = eth16i_close; + dev->hard_start_xmit = eth16i_tx; + dev->get_stats = eth16i_get_stats; + dev->set_multicast_list = ð16i_multicast; + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + boot = 0; + + return 0; } static void eth16i_initialize(struct device *dev) { - short ioaddr = dev->base_addr; - int i, node_w = 0; - unsigned char node_byte = 0; - - /* Setup station address */ - eth16i_select_regbank(NODE_ID_RB, ioaddr); - for(i = 0 ; i < 3 ; i++) { - unsigned short node_val = eth16i_read_eeprom(ioaddr, E_NODEID_0 + i); - ((unsigned short *)dev->dev_addr)[i] = ntohs(node_val); - } - - for(i = 0; i < 6; i++) { - outb( ((unsigned char *)dev->dev_addr)[i], ioaddr + NODE_ID_0 + i); - if(boot) { - printk("%02x", inb(ioaddr + NODE_ID_0 + i)); - if(i != 5) - printk(":"); - } - } - - /* Now we will set multicast addresses to accept none */ - eth16i_select_regbank(HASH_TABLE_RB, ioaddr); - for(i = 0; i < 8; i++) - outb(0x00, ioaddr + HASH_TABLE_0 + i); - - /* - Now let's disable the transmitter and receiver, set the buffer ram - cycle time, bus width and buffer data path width. Also we shall - set transmit buffer size and total buffer size. - */ - - eth16i_select_regbank(2, ioaddr); - - node_byte = 0; - node_w = eth16i_read_eeprom(ioaddr, E_PRODUCT_CFG); + int ioaddr = dev->base_addr; + int i, node_w = 0; + unsigned char node_byte = 0; + + /* Setup station address */ + eth16i_select_regbank(NODE_ID_RB, ioaddr); + for(i = 0 ; i < 3 ; i++) { + unsigned short node_val = eth16i_read_eeprom(ioaddr, E_NODEID_0 + i); + ((unsigned short *)dev->dev_addr)[i] = ntohs(node_val); + } - if( (node_w & 0xFF00) == 0x0800) - node_byte |= BUFFER_WIDTH_8; + for(i = 0; i < 6; i++) { + outb( ((unsigned char *)dev->dev_addr)[i], ioaddr + NODE_ID_0 + i); + if(boot) { + printk("%02x", inb(ioaddr + NODE_ID_0 + i)); + if(i != 5) + printk(":"); + } + } - node_byte |= _BS1; + /* Now we will set multicast addresses to accept none */ + eth16i_select_regbank(HASH_TABLE_RB, ioaddr); + for(i = 0; i < 8; i++) + outb(0x00, ioaddr + HASH_TABLE_0 + i); - if( (node_w & 0x00FF) == 64) - node_byte |= _BS0; - - node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2); + /* + Now let's disable the transmitter and receiver, set the buffer ram + cycle time, bus width and buffer data path width. Also we shall + set transmit buffer size and total buffer size. + */ - outb(node_byte, ioaddr + CONFIG_REG_0); + eth16i_select_regbank(2, ioaddr); - /* We shall halt the transmitting, if 16 collisions are detected */ - outb(RETRANS_AND_HALT_ON_16, ioaddr + COL_16_REG); + node_byte = 0; + node_w = eth16i_read_eeprom(ioaddr, E_PRODUCT_CFG); - if(boot) /* Now set port type */ - { - char *porttype[] = {"BNC", "DIX", "TP", "AUTO"}; - - ushort ptype = eth16i_read_eeprom(ioaddr, E_PORT_SELECT); - dev->if_port = (ptype & 0x00FF); - - printk(" %s interface.\n", porttype[dev->if_port]); + if( (node_w & 0xFF00) == 0x0800) + node_byte |= BUFFER_WIDTH_8; - if(ptype == E_PORT_AUTO) - ptype = eth16i_probe_port(ioaddr); - - eth16i_set_port(ioaddr, ptype); - } + node_byte |= SRAM_BS1; + + if( (node_w & 0x00FF) == 64) + node_byte |= SRAM_BS0; + + node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2); + + outb(node_byte, ioaddr + CONFIG_REG_0); + + /* We shall halt the transmitting, if 16 collisions are detected */ + outb(HALT_ON_16, ioaddr + COL_16_REG); + +#ifdef MODULE + /* if_port already set by init_module() */ +#else + dev->if_port = (dev->mem_start < E_PORT_FROM_EPROM) ? + dev->mem_start : E_PORT_FROM_EPROM; +#endif + + /* Set interface port type */ + if(boot) { + char *porttype[] = {"BNC", "DIX", "TP", "AUTO", "FROM_EPROM" }; + + switch(dev->if_port) + { + + case E_PORT_FROM_EPROM: + dev->if_port = eth16i_read_eeprom(ioaddr, E_PORT_SELECT); + break; + + case E_PORT_AUTO: + dev->if_port = eth16i_probe_port(ioaddr); + break; + + case E_PORT_BNC: + case E_PORT_TP: + case E_PORT_DIX: + break; + } + + printk(" %s interface.\n", porttype[dev->if_port]); + + eth16i_set_port(ioaddr, dev->if_port); + } - /* Set Receive Mode to normal operation */ - outb(MODE_2, ioaddr + RECEIVE_MODE_REG); + /* Set Receive Mode to normal operation */ + outb(MODE_2, ioaddr + RECEIVE_MODE_REG); } -static int eth16i_probe_port(short ioaddr) +static int eth16i_probe_port(int ioaddr) { - int i; - int retcode; - unsigned char dummy_packet[64] = { 0 }; + int i; + int retcode; + unsigned char dummy_packet[64] = { 0 }; - /* Powerup the chip */ - outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1); + /* Powerup the chip */ + outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1); - BITSET(ioaddr + CONFIG_REG_0, DLC_EN); + BITSET(ioaddr + CONFIG_REG_0, DLC_EN); - eth16i_select_regbank(NODE_ID_RB, ioaddr); + eth16i_select_regbank(NODE_ID_RB, ioaddr); - for(i = 0; i < 6; i++) { - dummy_packet[i] = inb(ioaddr + NODE_ID_0 + i); - dummy_packet[i+6] = inb(ioaddr + NODE_ID_0 + i); - } + for(i = 0; i < 6; i++) { + dummy_packet[i] = inb(ioaddr + NODE_ID_0 + i); + dummy_packet[i+6] = inb(ioaddr + NODE_ID_0 + i); + } - dummy_packet[12] = 0x00; - dummy_packet[13] = 0x04; + dummy_packet[12] = 0x00; + dummy_packet[13] = 0x04; - eth16i_select_regbank(2, ioaddr); + eth16i_select_regbank(2, ioaddr); - for(i = 0; i < 3; i++) { - BITSET(ioaddr + CONFIG_REG_0, DLC_EN); - BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); - eth16i_set_port(ioaddr, i); - - if(eth16i_debug > 1) - printk("Set port number %d\n", i); + for(i = 0; i < 3; i++) { + BITSET(ioaddr + CONFIG_REG_0, DLC_EN); + BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); + eth16i_set_port(ioaddr, i); + + if(eth16i_debug > 1) + printk(KERN_DEBUG "Set port number %d\n", i); + + retcode = eth16i_send_probe_packet(ioaddr, dummy_packet, 64); + if(retcode == 0) { + retcode = eth16i_receive_probe_packet(ioaddr); + if(retcode != -1) { + if(eth16i_debug > 1) + printk(KERN_DEBUG "Eth16i interface port found at %d\n", i); + return i; + } + } + else { + if(eth16i_debug > 1) + printk(KERN_DEBUG "TRANSMIT_DONE timeout when probing interface port\n"); + } + } - retcode = eth16i_send_probe_packet(ioaddr, dummy_packet, 64); - if(retcode == 0) { - retcode = eth16i_receive_probe_packet(ioaddr); - if(retcode != -1) { - if(eth16i_debug > 1) - printk("Eth16i interface port found at %d\n", i); - return i; - } - } - else { - if(eth16i_debug > 1) - printk("TRANSMIT_DONE timeout\n"); - } - } - - if( eth16i_debug > 1) - printk("Using default port\n"); - - return E_PORT_BNC; + if( eth16i_debug > 1) + printk(KERN_DEBUG "Using default port\n"); + + return E_PORT_BNC; } -static void eth16i_set_port(short ioaddr, int porttype) +static void eth16i_set_port(int ioaddr, int porttype) { - unsigned short temp = 0; + unsigned short temp = 0; - eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); - outb(LOOPBACK_CONTROL, ioaddr + TRANSMIT_MODE_REG); + eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); + outb(LOOPBACK_CONTROL, ioaddr + TRANSMIT_MODE_REG); - temp |= DIS_AUTO_PORT_SEL; + temp |= DIS_AUTO_PORT_SEL; - switch(porttype) { + switch(porttype) { - case E_PORT_BNC : - temp |= AUI_SELECT; - break; + case E_PORT_BNC : + temp |= AUI_SELECT; + break; - case E_PORT_TP : - break; - - case E_PORT_DIX : - temp |= AUI_SELECT; - BITSET(ioaddr + TRANSMIT_MODE_REG, CONTROL_OUTPUT); - break; - } - outb(temp, ioaddr + TRANSCEIVER_MODE_REG); + case E_PORT_TP : + break; + + case E_PORT_DIX : + temp |= AUI_SELECT; + BITSET(ioaddr + TRANSMIT_MODE_REG, CONTROL_OUTPUT); + break; + } + + outb(temp, ioaddr + TRANSCEIVER_MODE_REG); - if(eth16i_debug > 1) { - printk("TRANSMIT_MODE_REG = %x\n", inb(ioaddr + TRANSMIT_MODE_REG)); - printk("TRANSCEIVER_MODE_REG = %x\n", inb(ioaddr+TRANSCEIVER_MODE_REG)); - } + if(eth16i_debug > 1) { + printk(KERN_DEBUG "TRANSMIT_MODE_REG = %x\n", inb(ioaddr + TRANSMIT_MODE_REG)); + printk(KERN_DEBUG "TRANSCEIVER_MODE_REG = %x\n", + inb(ioaddr+TRANSCEIVER_MODE_REG)); + } } -static int eth16i_send_probe_packet(short ioaddr, unsigned char *b, int l) +static int eth16i_send_probe_packet(int ioaddr, unsigned char *b, int l) { - int starttime; + int starttime; - outb(0xff, ioaddr + TX_STATUS_REG); + outb(0xff, ioaddr + TX_STATUS_REG); - outw(l, ioaddr + DATAPORT); - outsw(ioaddr + DATAPORT, (unsigned short *)b, (l + 1) >> 1); - - starttime = jiffies; - outb(TX_START | 1, ioaddr + TRANSMIT_START_REG); + outw(l, ioaddr + DATAPORT); + outsw(ioaddr + DATAPORT, (unsigned short *)b, (l + 1) >> 1); - while( (inb(ioaddr + TX_STATUS_REG) & 0x80) == 0) { - if( (jiffies - starttime) > TIMEOUT_TICKS) { - break; - } - } - - return(0); + starttime = jiffies; + outb(TX_START | 1, ioaddr + TRANSMIT_START_REG); + + while( (inb(ioaddr + TX_STATUS_REG) & 0x80) == 0) { + if( (jiffies - starttime) > TX_TIMEOUT) { + return -1; + } + } + + return 0; } -static int eth16i_receive_probe_packet(short ioaddr) +static int eth16i_receive_probe_packet(int ioaddr) { - int starttime; - - starttime = jiffies; - - while((inb(ioaddr + TX_STATUS_REG) & 0x20) == 0) { - if( (jiffies - starttime) > TIMEOUT_TICKS) { - - if(eth16i_debug > 1) - printk("Timeout occurred waiting transmit packet received\n"); - starttime = jiffies; - while((inb(ioaddr + RX_STATUS_REG) & 0x80) == 0) { - if( (jiffies - starttime) > TIMEOUT_TICKS) { - if(eth16i_debug > 1) - printk("Timeout occurred waiting receive packet\n"); - return -1; - } - } - - if(eth16i_debug > 1) - printk("RECEIVE_PACKET\n"); - return(0); /* Found receive packet */ - } - } + int starttime; + + starttime = jiffies; - if(eth16i_debug > 1) { - printk("TRANSMIT_PACKET_RECEIVED %x\n", inb(ioaddr + TX_STATUS_REG)); - printk("RX_STATUS_REG = %x\n", inb(ioaddr + RX_STATUS_REG)); - } + while((inb(ioaddr + TX_STATUS_REG) & 0x20) == 0) { + if( (jiffies - starttime) > TX_TIMEOUT) { + + if(eth16i_debug > 1) + printk(KERN_DEBUG "Timeout occured waiting transmit packet received\n"); + starttime = jiffies; + while((inb(ioaddr + RX_STATUS_REG) & 0x80) == 0) { + if( (jiffies - starttime) > TX_TIMEOUT) { + if(eth16i_debug > 1) + printk(KERN_DEBUG "Timeout occured waiting receive packet\n"); + return -1; + } + } + + if(eth16i_debug > 1) + printk(KERN_DEBUG "RECEIVE_PACKET\n"); + return(0); /* Found receive packet */ + } + } + + if(eth16i_debug > 1) { + printk(KERN_DEBUG "TRANSMIT_PACKET_RECEIVED %x\n", inb(ioaddr + TX_STATUS_REG)); + printk(KERN_DEBUG "RX_STATUS_REG = %x\n", inb(ioaddr + RX_STATUS_REG)); + } - return(0); /* Return success */ + return(0); /* Return success */ } -static int eth16i_get_irq(short ioaddr) +#if 0 +static int eth16i_set_irq(struct device* dev) { - unsigned char cbyte; - - if( ioaddr < 0x1000) { - cbyte = inb(ioaddr + JUMPERLESS_CONFIG); - return( eth16i_irqmap[ ((cbyte & 0xC0) >> 6) ] ); - } else { /* Oh..the card is EISA so method getting IRQ different */ - unsigned short index = 0; - cbyte = inb(ioaddr + EISA_IRQ_REG); - while( (cbyte & 0x01) == 0) { - cbyte = cbyte >> 1; - index++; - } - return( eth32i_irqmap[ index ] ); - } + const int ioaddr = dev->base_addr; + const int irq = dev->irq; + int i = 0; + + if(ioaddr < 0x1000) { + while(eth16i_irqmap[i] && eth16i_irqmap[i] != irq) + i++; + + if(i < NUM_OF_ISA_IRQS) { + u8 cbyte = inb(ioaddr + JUMPERLESS_CONFIG); + cbyte = (cbyte & 0x3F) | (i << 6); + outb(cbyte, ioaddr + JUMPERLESS_CONFIG); + return 0; + } + } + else { + printk(KERN_NOTICE "%s: EISA Interrupt cannot be set. Use EISA Configuration utility.\n", dev->name); + } + + return -1; + } +#endif -static int eth16i_check_signature(short ioaddr) +static int eth16i_get_irq(int ioaddr) { - int i; - unsigned char creg[4] = { 0 }; - - for(i = 0; i < 4 ; i++) { + unsigned char cbyte; - creg[i] = inb(ioaddr + TRANSMIT_MODE_REG + i); - - if(eth16i_debug > 1) - printk("eth16i: read signature byte %x at %x\n", creg[i], - ioaddr + TRANSMIT_MODE_REG + i); - } + if( ioaddr < 0x1000) { + cbyte = inb(ioaddr + JUMPERLESS_CONFIG); + return( eth16i_irqmap[ ((cbyte & 0xC0) >> 6) ] ); + } else { /* Oh..the card is EISA so method getting IRQ different */ + unsigned short index = 0; + cbyte = inb(ioaddr + EISA_IRQ_REG); + while( (cbyte & 0x01) == 0) { + cbyte = cbyte >> 1; + index++; + } + return( eth32i_irqmap[ index ] ); + } +} - creg[0] &= 0x0F; /* Mask collision cnr */ - creg[2] &= 0x7F; /* Mask DCLEN bit */ +static int eth16i_check_signature(int ioaddr) +{ + int i; + unsigned char creg[4] = { 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 - to more strict chech still this have to be fixed. -*/ - if( !( (creg[0] == 0x06) && (creg[1] == 0x41)) ) { - if(creg[1] != 0x42) - return -1; - } -#endif + for(i = 0; i < 4 ; i++) { - if( !( (creg[2] == 0x36) && (creg[3] == 0xE0)) ) { - creg[2] &= 0x40; - creg[3] &= 0x03; + creg[i] = inb(ioaddr + TRANSMIT_MODE_REG + i); - if( !( (creg[2] == 0x40) && (creg[3] == 0x00)) ) - return -1; - } + if(eth16i_debug > 1) + printk("eth16i: read signature byte %x at %x\n", + creg[i], + ioaddr + TRANSMIT_MODE_REG + i); + } - if(eth16i_read_eeprom(ioaddr, E_NODEID_0) != 0) - return -1; - if((eth16i_read_eeprom(ioaddr, E_NODEID_1) & 0xFF00) != 0x4B00) - return -1; + creg[0] &= 0x0F; /* Mask collision cnr */ + creg[2] &= 0x7F; /* Mask DCLEN bit */ + +#ifdef 0 + /* + This was removed because the card was sometimes left to state + from which it couldn't be find anymore. If there is need + to more strict check still this have to be fixed. + */ + if( ! ((creg[0] == 0x06) && (creg[1] == 0x41)) ) { + if(creg[1] != 0x42) + return -1; + } +#endif - return 0; + if( !((creg[2] == 0x36) && (creg[3] == 0xE0)) ) { + creg[2] &= 0x40; + creg[3] &= 0x03; + + if( !((creg[2] == 0x40) && (creg[3] == 0x00)) ) + return -1; + } + + if(eth16i_read_eeprom(ioaddr, E_NODEID_0) != 0) + return -1; + + if((eth16i_read_eeprom(ioaddr, E_NODEID_1) & 0xFF00) != 0x4B00) + return -1; + + return 0; } static int eth16i_read_eeprom(int ioaddr, int offset) { - int data = 0; + int data = 0; - eth16i_eeprom_cmd(ioaddr, EEPROM_READ | offset); - outb(CS_1, ioaddr + EEPROM_CTRL_REG); - data = eth16i_read_eeprom_word(ioaddr); - outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG); + eth16i_eeprom_cmd(ioaddr, EEPROM_READ | offset); + outb(CS_1, ioaddr + EEPROM_CTRL_REG); + data = eth16i_read_eeprom_word(ioaddr); + outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG); - return(data); + return(data); } static int eth16i_read_eeprom_word(int ioaddr) { - int i; - int data = 0; - - for(i = 16; i > 0; i--) { - outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); - eeprom_slow_io(); - outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); - eeprom_slow_io(); - data = (data << 1) | ((inb(ioaddr + EEPROM_DATA_REG) & DI_1) ? 1 : 0); - eeprom_slow_io(); - } + int i; + int data = 0; + + for(i = 16; i > 0; i--) { + outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); + eeprom_slow_io(); + outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); + eeprom_slow_io(); + data = (data << 1) | + ((inb(ioaddr + EEPROM_DATA_REG) & DI_1) ? 1 : 0); + + eeprom_slow_io(); + } - return(data); + return(data); } static void eth16i_eeprom_cmd(int ioaddr, unsigned char command) { - int i; - - outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG); - outb(DI_0, ioaddr + EEPROM_DATA_REG); - outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); - outb(DI_1, ioaddr + EEPROM_DATA_REG); - outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); - - for(i = 7; i >= 0; i--) { - short cmd = ( (command & (1 << i)) ? DI_1 : DI_0 ); - outb(cmd, ioaddr + EEPROM_DATA_REG); - outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); - eeprom_slow_io(); - outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); - eeprom_slow_io(); - } + int i; + + outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG); + outb(DI_0, ioaddr + EEPROM_DATA_REG); + outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); + outb(DI_1, ioaddr + EEPROM_DATA_REG); + outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); + + for(i = 7; i >= 0; i--) { + short cmd = ( (command & (1 << i)) ? DI_1 : DI_0 ); + outb(cmd, ioaddr + EEPROM_DATA_REG); + outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); + eeprom_slow_io(); + outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); + eeprom_slow_io(); + } } static int eth16i_open(struct device *dev) { - struct eth16i_local *lp = (struct eth16i_local *)dev->priv; - int ioaddr = dev->base_addr; - - irq2dev_map[dev->irq] = dev; + struct eth16i_local *lp = (struct eth16i_local *)dev->priv; + int ioaddr = dev->base_addr; + + /* Powerup the chip */ + outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1); - /* Powerup the chip */ - outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1); - - /* Initialize the chip */ - eth16i_initialize(dev); - - /* Set the transmit buffer size */ - lp->tx_buf_size = eth16i_tx_buf_map[ETH16I_TX_BUF_SIZE & 0x03]; - - if(eth16i_debug > 3) - printk("%s: transmit buffer size %d\n", dev->name, lp->tx_buf_size); + /* Initialize the chip */ + eth16i_initialize(dev); - /* Now enable Transmitter and Receiver sections */ - BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); - - /* Now switch to register bank 2, for run time operation */ - eth16i_select_regbank(2, ioaddr); + /* Set the transmit buffer size */ + lp->tx_buf_size = eth16i_tx_buf_map[ETH16I_TX_BUF_SIZE & 0x03]; - lp->open_time = jiffies; - lp->tx_started = 0; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - - /* Turn on interrupts*/ - outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); - - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; + if(eth16i_debug > 0) + printk(KERN_DEBUG "%s: transmit buffer size %d\n", + dev->name, lp->tx_buf_size); -#ifdef MODULE - MOD_INC_USE_COUNT; -#endif + /* Now enable Transmitter and Receiver sections */ + BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); + + /* Now switch to register bank 2, for run time operation */ + eth16i_select_regbank(2, ioaddr); - return 0; + lp->open_time = jiffies; + lp->tx_started = 0; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + + /* Turn on interrupts*/ + outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + MOD_INC_USE_COUNT; + + return 0; } static int eth16i_close(struct device *dev) { - struct eth16i_local *lp = (struct eth16i_local *)dev->priv; - int ioaddr = dev->base_addr; + struct eth16i_local *lp = (struct eth16i_local *)dev->priv; + int ioaddr = dev->base_addr; - lp->open_time = 0; + eth16i_reset(dev); - dev->tbusy = 1; - dev->start = 0; + /* Turn off interrupts*/ + outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); - /* Disable transmit and receive */ - BITSET(ioaddr + CONFIG_REG_0, DLC_EN); + dev->start = 0; + dev->tbusy = 1; - /* Reset the chip */ - outb(0xff, ioaddr + RESET); + lp->open_time = 0; - /* Save some energy by switching off power */ - BITCLR(ioaddr + CONFIG_REG_1, POWERUP); + /* Disable transmit and receive */ + BITSET(ioaddr + CONFIG_REG_0, DLC_EN); -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif + /* Reset the chip */ + /* outb(0xff, ioaddr + RESET); */ + /* outw(0xffff, ioaddr + TX_STATUS_REG); */ + + outb(0x00, ioaddr + CONFIG_REG_1); + + MOD_DEC_USE_COUNT; - return 0; + return 0; } static int eth16i_tx(struct sk_buff *skb, struct device *dev) { - struct eth16i_local *lp = (struct eth16i_local *)dev->priv; - int ioaddr = dev->base_addr; - - if(dev->tbusy) { - /* - If we get here, some higher level has decided that we are broken. - There should really be a "kick me" function call instead. - */ - - int tickssofar = jiffies - dev->trans_start; - if(tickssofar < TIMEOUT_TICKS) /* Let's not rush with our timeout, */ - return 1; /* wait a couple of ticks first */ + struct eth16i_local *lp = (struct eth16i_local *)dev->priv; + int ioaddr = dev->base_addr; + int status = 0; + + if(dev->tbusy) { + + /* + If we get here, some higher level has decided that + we are broken. There should really be a "kick me" + function call instead. + */ + + int tickssofar = jiffies - dev->trans_start; + if(tickssofar < TX_TIMEOUT) + return 1; + + outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); + + printk(KERN_WARNING "%s: transmit timed out with status %04x, %s ?\n", + dev->name, + inw(ioaddr + TX_STATUS_REG), + (inb(ioaddr + TX_STATUS_REG) & TX_DONE) ? + "IRQ conflict" : "network cable problem"); + + dev->trans_start = jiffies; + + /* Let's dump all registers */ + if(eth16i_debug > 0) { + printk(KERN_DEBUG "%s: timeout: %02x %02x %02x %02x %02x %02x %02x %02x.\n", + dev->name, inb(ioaddr + 0), + inb(ioaddr + 1), inb(ioaddr + 2), + inb(ioaddr + 3), inb(ioaddr + 4), + inb(ioaddr + 5), + inb(ioaddr + 6), inb(ioaddr + 7)); + + printk(KERN_DEBUG "%s: transmit start reg: %02x. collision reg %02x\n", + dev->name, inb(ioaddr + TRANSMIT_START_REG), + inb(ioaddr + COL_16_REG)); + + printk(KERN_DEBUG "lp->tx_queue = %d\n", lp->tx_queue); + printk(KERN_DEBUG "lp->tx_queue_len = %d\n", lp->tx_queue_len); + printk(KERN_DEBUG "lp->tx_started = %d\n", lp->tx_started); + + } - printk("%s: transmit timed out with status %04x, %s ?\n", dev->name, - inw(ioaddr + TX_STATUS_REG), - (inb(ioaddr + TX_STATUS_REG) & TX_DONE) ? - "IRQ conflict" : "network cable problem"); + lp->stats.tx_errors++; - /* Let's dump all registers */ - if(eth16i_debug > 0) { - printk("%s: timeout regs: %02x %02x %02x %02x %02x %02x %02x %02x.\n", - dev->name, inb(ioaddr + 0), inb(ioaddr + 1), inb(ioaddr + 2), - inb(ioaddr + 3), inb(ioaddr + 4), inb(ioaddr + 5), - inb(ioaddr + 6), inb(ioaddr + 7)); + eth16i_reset(dev); + dev->trans_start = jiffies; - printk("lp->tx_queue = %d\n", lp->tx_queue); - printk("lp->tx_queue_len = %d\n", lp->tx_queue_len); - printk("lp->tx_started = %d\n", lp->tx_started); + outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); - } + } - lp->stats.tx_errors++; + /* + If some higher layer thinks we've missed an tx-done interrupt + we are passed NULL. Caution: dev_tint() handles the cli()/sti() + itself + */ + + if(skb == NULL) { +#if LINUX_VERSION_CODE < 0x020100 + dev_tint(dev); +#endif + if(eth16i_debug > 0) + printk(KERN_WARNING "%s: Missed tx-done interrupt.\n", dev->name); + return 0; + } - /* Now let's try to restart the adaptor */ - - BITSET(ioaddr + CONFIG_REG_0, DLC_EN); - outw(0xffff, ioaddr + RESET); - eth16i_initialize(dev); - outw(0xffff, ioaddr + TX_STATUS_REG); - BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); - - lp->tx_started = 0; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - - outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); - - dev->tbusy = 0; - dev->trans_start = jiffies; - } - - /* - If some higher layer thinks we've missed an tx-done interrupt - we are passed NULL. Caution: dev_tint() handles the cli()/sti() - itself - */ - if(skb == NULL) { - dev_tint(dev); - return 0; - } - - /* Block a timer based transmitter from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ - - /* Turn off TX interrupts */ - outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); - - if(set_bit(0, (void *)&dev->tbusy) != 0) - printk("%s: Transmitter access conflict.\n", dev->name); - else { - short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned char *buf = skb->data; - - outw(length, ioaddr + DATAPORT); - - if( ioaddr < 0x1000 ) - outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); - else { - unsigned char frag = length % 4; + /* Block a timer based transmitter from overlapping. + This could better be done with atomic_swap(1, dev->tbusy), + but set_bit() works as well. */ - outsl(ioaddr + DATAPORT, buf, length >> 2); + set_bit(0, (void *)&lp->tx_buf_busy); - if( frag != 0 ) { - outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1); - if( frag == 3 ) - outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC) + 2), 1); - } - } - - lp->tx_queue++; - lp->tx_queue_len += length + 2; - - if(lp->tx_started == 0) { - /* If the transmitter is idle..always trigger a transmit */ - outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG); - lp->tx_queue = 0; - lp->tx_queue_len = 0; - dev->trans_start = jiffies; - lp->tx_started = 1; - dev->tbusy = 0; - } - else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) { - /* There is still more room for one more packet in tx buffer */ - dev->tbusy = 0; - } - - outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); - - /* Turn TX interrupts back on */ - /* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */ - } - dev_kfree_skb(skb, FREE_WRITE); + /* Turn off TX interrupts */ + outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); + + if(test_and_set_bit(0, (void *)&dev->tbusy) != 0) { + printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); + status = -1; + } + else { + ushort length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned char *buf = skb->data; + + if( (length + 2) > (lp->tx_buf_size - lp->tx_queue_len)) { + if(eth16i_debug > 0) + printk(KERN_WARNING "%s: Transmit buffer full.\n", dev->name); + } + else { + outw(length, ioaddr + DATAPORT); + + if( ioaddr < 0x1000 ) + outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); + else { + unsigned char frag = length % 4; + + outsl(ioaddr + DATAPORT, buf, length >> 2); + + if( frag != 0 ) { + outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1); + if( frag == 3 ) + outsw(ioaddr + DATAPORT, + (buf + (length & 0xFFFC) + 2), 1); + } + } + + lp->tx_buffered_packets++; + lp->tx_queue++; + lp->tx_queue_len += length + 2; + + } + + lp->tx_buf_busy = 0; + + if(lp->tx_started == 0) { + /* If the transmitter is idle..always trigger a transmit */ + outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG); + lp->tx_queue = 0; + lp->tx_queue_len = 0; + dev->trans_start = jiffies; + lp->tx_started = 1; + dev->tbusy = 0; + } + else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) { + /* There is still more room for one more packet in tx buffer */ + dev->tbusy = 0; + } + + outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); + + /* Turn TX interrupts back on */ + /* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */ + status = 0; + } + +#if LINUX_VERSION_CODE >= 0x020100 + dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif - return 0; + return status; } static void eth16i_rx(struct device *dev) { - struct eth16i_local *lp = (struct eth16i_local *)dev->priv; - int ioaddr = dev->base_addr; - int boguscount = MAX_RX_LOOP; + struct eth16i_local *lp = (struct eth16i_local *)dev->priv; + int ioaddr = dev->base_addr; + int boguscount = MAX_RX_LOOP; + + /* Loop until all packets have been read */ + while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) { + + /* Read status byte from receive buffer */ + ushort status = inw(ioaddr + DATAPORT); + + /* Get the size of the packet from receive buffer */ + ushort pkt_len = inw(ioaddr + DATAPORT); + + if(eth16i_debug > 4) + printk(KERN_DEBUG "%s: Receiving packet mode %02x status %04x.\n", + dev->name, + inb(ioaddr + RECEIVE_MODE_REG), status); + + if( !(status & PKT_GOOD) ) { + lp->stats.rx_errors++; + + if( (pkt_len < ETH_ZLEN) || (pkt_len > ETH_FRAME_LEN) ) { + lp->stats.rx_length_errors++; + eth16i_reset(dev); + return; + } + else { + eth16i_skip_packet(dev); + lp->stats.rx_dropped++; + } + } + else { /* Ok so now we should have a good packet */ + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_len + 3); + if( skb == NULL ) { + printk(KERN_WARNING "%s: Could'n allocate memory for packet (len %d)\n", + dev->name, pkt_len); + eth16i_skip_packet(dev); + lp->stats.rx_dropped++; + break; + } + + skb->dev = dev; + skb_reserve(skb,2); + + /* + Now let's get the packet out of buffer. + size is (pkt_len + 1) >> 1, cause we are now reading words + and it have to be even aligned. + */ + + if(ioaddr < 0x1000) + insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), + (pkt_len + 1) >> 1); + else { + unsigned char *buf = skb_put(skb, pkt_len); + unsigned char frag = pkt_len % 4; + + insl(ioaddr + DATAPORT, buf, pkt_len >> 2); + + if(frag != 0) { + unsigned short rest[2]; + rest[0] = inw( ioaddr + DATAPORT ); + if(frag == 3) + rest[1] = inw( ioaddr + DATAPORT ); + + memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag); + } + } + + skb->protocol=eth_type_trans(skb, dev); + netif_rx(skb); + lp->stats.rx_packets++; + + if( eth16i_debug > 5 ) { + int i; + printk(KERN_DEBUG "%s: Received packet of length %d.\n", + dev->name, pkt_len); + for(i = 0; i < 14; i++) + printk(KERN_DEBUG " %02x", skb->data[i]); + printk(KERN_DEBUG ".\n"); + } - /* Loop until all packets have been read */ - while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) { - - /* Read status byte from receive buffer */ - ushort status = inw(ioaddr + DATAPORT); + } /* else */ - if(eth16i_debug > 4) - printk("%s: Receiving packet mode %02x status %04x.\n", - dev->name, inb(ioaddr + RECEIVE_MODE_REG), status); - - if( !(status & PKT_GOOD) ) { - /* Hmm..something went wrong. Let's check what error occurred */ - lp->stats.rx_errors++; - if( status & PKT_SHORT ) lp->stats.rx_length_errors++; - if( status & PKT_ALIGN_ERR ) lp->stats.rx_frame_errors++; - if( status & PKT_CRC_ERR ) lp->stats.rx_crc_errors++; - if( status & PKT_RX_BUF_OVERFLOW) lp->stats.rx_over_errors++; - } - else { /* Ok so now we should have a good packet */ - struct sk_buff *skb; - - /* Get the size of the packet from receive buffer */ - ushort pkt_len = inw(ioaddr + DATAPORT); - - if(pkt_len > ETH_FRAME_LEN) { - printk("%s: %s claimed a very large packet, size of %d bytes.\n", - dev->name, cardname, pkt_len); - outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG); - lp->stats.rx_dropped++; - break; - } - - skb = dev_alloc_skb(pkt_len + 3); - if( skb == NULL ) { - printk("%s: Couldn't allocate memory for packet (len %d)\n", - dev->name, pkt_len); - outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG); - lp->stats.rx_dropped++; - break; - } - - skb->dev = dev; - skb_reserve(skb,2); - /* - Now let's get the packet out of buffer. - size is (pkt_len + 1) >> 1, cause we are now reading words - and it have to be even aligned. - */ - - if( ioaddr < 0x1000) - insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), (pkt_len + 1) >> 1); - else { - unsigned char *buf = skb_put(skb, pkt_len); - unsigned char frag = pkt_len % 4; + if(--boguscount <= 0) + break; - insl(ioaddr + DATAPORT, buf, pkt_len >> 2); - - if(frag != 0) { - unsigned short rest[2]; - rest[0] = inw( ioaddr + DATAPORT ); - if(frag == 3) - rest[1] = inw( ioaddr + DATAPORT ); + } /* while */ + +#if 0 + { + int i; + + for(i = 0; i < 20; i++) { + if( (inb(ioaddr+RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == + RX_BUFFER_EMPTY) + break; + inw(ioaddr + DATAPORT); + outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG); + } - memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag); - } + if(eth16i_debug > 1) + printk(KERN_DEBUG "%s: Flushed receive buffer.\n", dev->name); } - - skb->protocol=eth_type_trans(skb, dev); - netif_rx(skb); - lp->stats.rx_packets++; - - if( eth16i_debug > 5 ) { - int i; - printk("%s: Received packet of length %d.\n", dev->name, pkt_len); - for(i = 0; i < 14; i++) - printk(" %02x", skb->data[i]); - printk(".\n"); +#endif + + return; +} + +static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = dev_id; + struct eth16i_local *lp; + int ioaddr = 0, + status; + + if(dev == NULL) { + printk(KERN_WARNING "eth16i_interrupt(): irq %d for unknown device. \n", irq); + return; } - - } /* else */ - if(--boguscount <= 0) - break; + /* Turn off all interrupts from adapter */ + outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); - } /* while */ + set_bit(0, (void *)&dev->tbusy); /* Set the device busy so that */ + /* eth16i_tx wont be called */ -#if 0 - { - int i; + if(dev->interrupt) + printk(KERN_WARNING "%s: Re-entering the interrupt handler.\n", dev->name); + dev->interrupt = 1; + + ioaddr = dev->base_addr; + lp = (struct eth16i_local *)dev->priv; + status = inw(ioaddr + TX_STATUS_REG); /* Get the status */ + outw(status, ioaddr + TX_STATUS_REG); /* Clear status bits */ + + if(eth16i_debug > 3) + printk(KERN_DEBUG "%s: Interrupt with status %04x.\n", dev->name, status); + + if( status & 0x7f00 ) { + + lp->stats.rx_errors++; + + if(status & (BUS_RD_ERR << 8) ) + printk(KERN_WARNING "%s: Bus read error.\n",dev->name); + if(status & (SHORT_PKT_ERR << 8) ) lp->stats.rx_length_errors++; + if(status & (ALIGN_ERR << 8) ) lp->stats.rx_frame_errors++; + if(status & (CRC_ERR << 8) ) lp->stats.rx_crc_errors++; + if(status & (RX_BUF_OVERFLOW << 8) ) lp->stats.rx_over_errors++; + } + if( status & 0x001a) { + + lp->stats.tx_errors++; + + if(status & CR_LOST) lp->stats.tx_carrier_errors++; + if(status & TX_JABBER_ERR) lp->stats.tx_window_errors++; - for(i = 0; i < 20; i++) { - if( (inb(ioaddr+RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == RX_BUFFER_EMPTY) - break; - inw(ioaddr + DATAPORT); - outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG); - } - - if(eth16i_debug > 1) - printk("%s: Flushed receive buffer.\n", dev->name); - } +#if 0 + if(status & COLLISION) { + lp->stats.collisions += + ((inb(ioaddr+TRANSMIT_MODE_REG) & 0xF0) >> 4); + } #endif + if(status & COLLISIONS_16) { + if(lp->col_16 < MAX_COL_16) { + lp->col_16++; + lp->stats.collisions++; + /* Resume transmitting, skip failed packet */ + outb(0x02, ioaddr + COL_16_REG); + } + else { + printk(KERN_WARNING "%s: bailing out due to many consecutive 16-in-a-row collisions. Network cable problem?\n", dev->name); + } + } + } + + if( status & 0x00ff ) { /* Let's check the transmit status reg */ + + if(status & TX_DONE) { /* The transmit has been done */ + lp->stats.tx_packets = lp->tx_buffered_packets; + lp->col_16 = 0; + + if(lp->tx_queue) { /* Is there still packets ? */ + /* There was packet(s) so start transmitting and write also + how many packets there is to be sended */ + outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG); + lp->tx_queue = 0; + lp->tx_queue_len = 0; + lp->tx_started = 1; + dev->trans_start = jiffies; + mark_bh(NET_BH); + } + else { + lp->tx_started = 0; + mark_bh(NET_BH); + } + } + } + + if( ( status & 0x8000 ) || + ( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) { + eth16i_rx(dev); /* We have packet in receive buffer */ + } + + dev->interrupt = 0; + + /* Turn interrupts back on */ + outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); + + if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) { + /* There is still more room for one more packet in tx buffer */ + dev->tbusy = 0; + } + + return; +} + +static void eth16i_skip_packet(struct device *dev) +{ + int ioaddr = dev->base_addr; - return; + inw(ioaddr + DATAPORT); + inw(ioaddr + DATAPORT); + inw(ioaddr + DATAPORT); + + outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG); + while( inb( ioaddr + FILTER_SELF_RX_REG ) != 0); } -static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static void eth16i_reset(struct device *dev) { - struct device *dev = (struct device *)(irq2dev_map[irq]); - struct eth16i_local *lp; - int ioaddr = 0, - status; - - if(dev == NULL) { - printk("eth16i_interrupt(): irq %d for unknown device. \n", irq); - return; - } - - /* Turn off all interrupts from adapter */ - outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); - - dev->interrupt = 1; - - ioaddr = dev->base_addr; - lp = (struct eth16i_local *)dev->priv; - status = inw(ioaddr + TX_STATUS_REG); /* Get the status */ - outw(status, ioaddr + TX_STATUS_REG); /* Clear status bits */ - - if(eth16i_debug > 3) - printk("%s: Interrupt with status %04x.\n", dev->name, status); + struct eth16i_local *lp = (struct eth16i_local *)dev->priv; + int ioaddr = dev->base_addr; - if( status & 0x00ff ) { /* Let's check the transmit status reg */ - - if(status & TX_DONE) { /* The transmit has been done */ - lp->stats.tx_packets++; + if(eth16i_debug > 1) + printk(KERN_DEBUG "%s: Resetting device.\n", dev->name); - if(lp->tx_queue) { /* Is there still packets ? */ - /* There was packet(s) so start transmitting and write also - how many packets there is to be sent */ - outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG); - lp->tx_queue = 0; - lp->tx_queue_len = 0; - dev->trans_start = jiffies; - dev->tbusy = 0; - mark_bh(NET_BH); - } - else { - lp->tx_started = 0; - dev->tbusy = 0; - mark_bh(NET_BH); - } - } - } - - if( ( status & 0xff00 ) || - ( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) { - eth16i_rx(dev); /* We have packet in receive buffer */ - } + BITSET(ioaddr + CONFIG_REG_0, DLC_EN); + outw(0xffff, ioaddr + TX_STATUS_REG); + eth16i_select_regbank(2, ioaddr); - dev->interrupt = 0; - - /* Turn interrupts back on */ - outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); + lp->tx_started = 0; + lp->tx_buf_busy = 0; + lp->tx_queue = 0; + lp->tx_queue_len = 0; - return; + dev->interrupt = 0; + dev->start = 1; + dev->tbusy = 0; + BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); } static void eth16i_multicast(struct device *dev) { - short ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; - if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) - { - dev->flags|=IFF_PROMISC; /* Must do this */ - outb(3, ioaddr + RECEIVE_MODE_REG); - } else { - outb(2, ioaddr + RECEIVE_MODE_REG); - } + if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) + { + dev->flags|=IFF_PROMISC; /* Must do this */ + outb(3, ioaddr + RECEIVE_MODE_REG); + } else { + outb(2, ioaddr + RECEIVE_MODE_REG); + } } static struct enet_statistics *eth16i_get_stats(struct device *dev) { - struct eth16i_local *lp = (struct eth16i_local *)dev->priv; + struct eth16i_local *lp = (struct eth16i_local *)dev->priv; - return &lp->stats; + return &lp->stats; } -static void eth16i_select_regbank(unsigned char banknbr, short ioaddr) +static void eth16i_select_regbank(unsigned char banknbr, int ioaddr) { - unsigned char data; + unsigned char data; - data = inb(ioaddr + CONFIG_REG_1); - outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1); + data = inb(ioaddr + CONFIG_REG_1); + outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1); } #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_eth16i = { - devicename, - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, eth16i_probe }; -int io = 0x2a0; -int irq = 0; +static ushort eth16i_parse_mediatype(const char* s) +{ + if(!s) + return E_PORT_FROM_EPROM; + + if (!strncmp(s, "bnc", 3)) + return E_PORT_BNC; + else if (!strncmp(s, "tp", 2)) + return E_PORT_TP; + else if (!strncmp(s, "dix", 3)) + return E_PORT_DIX; + else if (!strncmp(s, "auto", 4)) + return E_PORT_AUTO; + else + return E_PORT_FROM_EPROM; +} + +#define MAX_ETH16I_CARDS 4 /* Max number of Eth16i cards per module */ +#define NAMELEN 8 /* number of chars for storing dev->name */ + +static char namelist[NAMELEN * MAX_ETH16I_CARDS] = { 0, }; +static struct device dev_eth16i[MAX_ETH16I_CARDS] = { + { + NULL, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int ioaddr[MAX_ETH16I_CARDS] = { 0, }; +#if 0 +static int irq[MAX_ETH16I_CARDS] = { 0, }; +#endif +static char* mediatype[MAX_ETH16I_CARDS] = { 0, }; +static int debug = -1; + +#if (LINUX_VERSION_CODE >= 0x20115) +MODULE_AUTHOR("Mika Kuoppala "); +MODULE_DESCRIPTION("ICL EtherTeam 16i/32 driver"); + +MODULE_PARM(ioaddr, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "i"); +MODULE_PARM_DESC(ioaddr, "eth16i io base address"); + +#if 0 +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "i"); +MODULE_PARM_DESC(irq, "eth16i interrupt request number"); +#endif + +MODULE_PARM(mediatype, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "s"); +MODULE_PARM_DESC(mediatype, "eth16i interfaceport mediatype"); + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "eth16i debug level (0-4)"); +#endif int init_module(void) { - if(io == 0) - printk("eth16i: You should not use auto-probing with insmod!\n"); + int this_dev, found = 0; + + for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) + { + struct device *dev = &dev_eth16i[this_dev]; - dev_eth16i.base_addr = io; - dev_eth16i.irq = irq; - if( register_netdev( &dev_eth16i ) != 0 ) { - printk("eth16i: register_netdev() returned non-zero.\n"); - return -EIO; - } + dev->name = namelist + (NAMELEN*this_dev); + dev->irq = 0; /* irq[this_dev]; */ + dev->base_addr = ioaddr[this_dev]; + dev->init = eth16i_probe; + + if(debug != -1) + eth16i_debug = debug; + + if(eth16i_debug > 1) + printk(KERN_NOTICE "eth16i(%d): interface type %s\n", this_dev, mediatype[this_dev] ? mediatype[this_dev] : "none" ); + + dev->if_port = eth16i_parse_mediatype(mediatype[this_dev]); + + if(ioaddr[this_dev] == 0) + { + if(this_dev != 0) break; /* Only autoprobe 1st one */ + + printk(KERN_NOTICE "eth16i.c: Presently autoprobing (not recommended) for a single card.\n"); + } + + if(register_netdev(dev) != 0) + { + printk(KERN_WARNING "eth16i.c No Eth16i card found (i/o = 0x%x).\n", + ioaddr[this_dev]); + + if(found != 0) return 0; + return -ENXIO; + } + found++; + } return 0; } - + void cleanup_module(void) { - unregister_netdev( &dev_eth16i ); - free_irq( dev_eth16i.irq, NULL ); - irq2dev_map[ dev_eth16i.irq ] = NULL; - release_region( dev_eth16i.base_addr, ETH16I_IO_EXTENT ); -} + int this_dev; + for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) + { + struct device* dev = &dev_eth16i[this_dev]; + + if(dev->priv != NULL) + { + unregister_netdev(dev); + kfree(dev->priv); + dev->priv = NULL; + + free_irq(dev->irq, dev); + release_region(dev->base_addr, ETH16I_IO_EXTENT); + + } + } +} #endif /* MODULE */ +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c eth16i.c" + * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict -prototypes -O6 -c eth16i.c" + * tab-width: 8 + * c-basic-offset: 8 + * c-indent-level: 8 + * End: + */ +/* End of file eth16i.c */ diff -u --recursive --new-file v2.0.35/linux/drivers/net/hp100.c linux/drivers/net/hp100.c --- v2.0.35/linux/drivers/net/hp100.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/hp100.c Sun Nov 15 10:33:03 1998 @@ -1044,7 +1044,7 @@ /* * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and - * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded + * 4 bytes for header). We will leave NUM_RXPDLS * 508 (rounded * to the next higher 1k boundary) bytes for the rx-pdl's * Note: For non-etr chips the transmit stop register must be * programmed on a 1k boundary, i.e. bits 9:0 must be zero. diff -u --recursive --new-file v2.0.35/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c --- v2.0.35/linux/drivers/net/ibmtr.c Tue Dec 2 13:52:31 1997 +++ linux/drivers/net/ibmtr.c Sun Nov 15 10:33:03 1998 @@ -439,9 +439,9 @@ /* We must figure out how much shared memory space this adapter will occupy so that if there are two adapters we can fit both in. Given a choice, we will limit this adapter to 32K. The - maximum space will will use for two adapters is 64K so if the - adapter we are working on demands 64K (it also doesn't support - paging), then only one adapter can be supported. */ + maximum space will use for two adapters is 64K so if the adapter + we are working on demands 64K (it also doesn't support paging), + then only one adapter can be supported. */ /* determine how much of total RAM is mapped into PC space */ ti->mapped_ram_size=1<<((((readb(ti->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD)) >>2) & 0x03) + 4); diff -u --recursive --new-file v2.0.35/linux/drivers/net/lance.c linux/drivers/net/lance.c --- v2.0.35/linux/drivers/net/lance.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/lance.c Sun Nov 15 10:33:03 1998 @@ -297,7 +297,7 @@ static int lance_open(struct device *dev); static int lance_open_fail(struct device *dev); -static void lance_init_ring(struct device *dev); +static void lance_init_ring(struct device *dev, int mode); static int lance_start_xmit(struct sk_buff *skb, struct device *dev); static int lance_rx(struct device *dev); static void lance_interrupt(int irq, void *dev_id, struct pt_regs *regs); @@ -360,7 +360,7 @@ if (dev->priv != NULL) { kfree(dev->priv); dev->priv = NULL; - free_irq(dev->irq, NULL); + free_dma(dev->dma); release_region(dev->base_addr, LANCE_TOTAL_SIZE); unregister_netdev(dev); } @@ -512,14 +512,6 @@ dev->base_addr = ioaddr; request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name); -#ifdef CONFIG_LANCE32 - /* look if it's a PCI or VLB chip */ - if (lance_version == PCNET_PCI || lance_version == PCNET_VLB || lance_version == PCNET_PCI_II) { - extern int lance32_probe1 (struct device *dev, const char *chipname, int pci_irq_line); - - return lance32_probe1 (dev, chipname, pci_irq_line); - } -#endif /* Make certain the data structures used by the LANCE are aligned and DMAble. */ lp = (struct lance_private *)(((unsigned long)kmalloc(sizeof(*lp)+7, @@ -746,7 +738,7 @@ (u32) virt_to_bus(lp->rx_ring), (u32) virt_to_bus(&lp->init_block)); - lance_init_ring(dev); + lance_init_ring(dev, GFP_KERNEL); /* Re-initialize the LANCE, and start it when done. */ outw(0x0001, ioaddr+LANCE_ADDR); outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); @@ -808,7 +800,7 @@ /* Initialize the LANCE Rx and Tx rings. */ static void -lance_init_ring(struct device *dev) +lance_init_ring(struct device *dev, int gfp) { struct lance_private *lp = (struct lance_private *)dev->priv; int i; @@ -821,13 +813,13 @@ struct sk_buff *skb; void *rx_buff; - skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | GFP_KERNEL); + skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp); lp->rx_skbuff[i] = skb; if (skb) { skb->dev = dev; rx_buff = skb->tail; } else - rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | GFP_KERNEL); + rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp); if (rx_buff == NULL) lp->rx_ring[i].base = 0; else @@ -858,7 +850,7 @@ if (must_reinit || (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) { lance_purge_tx_ring(dev); - lance_init_ring(dev); + lance_init_ring(dev, GFP_ATOMIC); } outw(0x0000, dev->base_addr + LANCE_ADDR); outw(csr0_bits, dev->base_addr + LANCE_DATA); diff -u --recursive --new-file v2.0.35/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c --- v2.0.35/linux/drivers/net/ne2k-pci.c Sun Nov 15 10:49:39 1998 +++ linux/drivers/net/ne2k-pci.c Sun Nov 15 10:33:03 1998 @@ -30,7 +30,6 @@ #include #endif #include -#include #include #include #include diff -u --recursive --new-file v2.0.35/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c --- v2.0.35/linux/drivers/net/pcnet32.c Mon Jul 13 13:46:29 1998 +++ linux/drivers/net/pcnet32.c Sun Nov 15 10:33:03 1998 @@ -27,7 +27,6 @@ #define PCNET_LOG_TX_BUFFERS 4 #define PCNET_LOG_RX_BUFFERS 4 -#include #ifdef MODULE #ifdef MODVERSIONS #include @@ -179,7 +178,8 @@ {0x2420, "PCnet/PCI 79C970", 0}, {0x2430, "PCnet32", 0}, {0x2621, "PCnet/PCI II 79C970A", 0}, - {0x2623, "PCnet/PCI II 79C971A", 0}, + {0x2623, "PCnet/FAST 79C971", 0}, + {0x2624, "PCnet/FAST+ 79C972", 0}, {0x0, "PCnet32 (unknown)", 0}, }; @@ -242,11 +242,6 @@ PCI_COMMAND, new_command); } -#ifdef __powerpc__ - /* This is bogus! -djb */ - irq_line = 15; -#endif - if (pcnet32_probe1(dev, pci_ioaddr, irq_line) != 0) { /* Should never happen. */ printk(KERN_ERR "pcnet32.c: Probe of PCI card at %#x failed.\n", @@ -305,6 +300,8 @@ The first six bytes are the station address. */ for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + + printk("\n"); dev->base_addr = ioaddr; request_region(ioaddr, PCNET32_TOTAL_SIZE, dev->name); @@ -344,15 +341,7 @@ outw(0x0000, ioaddr+PCNET32_ADDR); inw(ioaddr+PCNET32_ADDR); - dev->irq = irq_line; -#ifdef __powerpc__ - /* This is sooo bogus! -djb */ - irq_line = 15; -#endif - - outw(0x0002, ioaddr+PCNET32_ADDR); - /* only touch autoselect bit */ - outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF); + dev->irq = irq_line; if (pcnet32_debug > 0) printk(version); @@ -403,7 +392,17 @@ (u32) virt_to_bus(lp->rx_ring), (u32) virt_to_bus(&lp->init_block)); - lp->init_block.mode = 0x0000; + /* check for ATLAS T1/E1 LAW card */ + if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75) { + /* select GPSI mode */ + lp->init_block.mode = 0x0100; + outw(0x0002, ioaddr+PCNET32_ADDR); + outw(inw(ioaddr+PCNET32_BUS_IF) & ~2, ioaddr+PCNET32_BUS_IF); + /* switch full duplex on */ + outw(0x0009, ioaddr+PCNET32_ADDR); + outw(inw(ioaddr+PCNET32_BUS_IF) | 1, ioaddr+PCNET32_BUS_IF); + } else + lp->init_block.mode = 0x0000; lp->init_block.filter[0] = 0x00000000; lp->init_block.filter[1] = 0x00000000; pcnet32_init_ring(dev); @@ -718,7 +717,22 @@ /* Log misc errors. */ if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */ - if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */ + if (csr0 & 0x1000) { + /* + * this happens when our receive ring is full. This + * shouldn't be a problem as we will see normal rx + * interrupts for the frames in the receive ring. But + * there are some PCI chipsets (I can reproduce this + * on SP3G with Intel saturn chipset) which have some- + * times problems and will fill up the receive ring + * with error descriptors. In this situation we don't + * get a rx interrupt, but a missed frame interrupt + * sooner or later. So we try to clean up our receive + * ring here. + */ + pcnet32_rx(dev); + lp->stats.rx_errors++; /* Missed a Rx frame. */ + } if (csr0 & 0x0800) { printk("%s: Bus master arbitration failure, status %4.4x.\n", dev->name, csr0); @@ -878,14 +892,14 @@ if (dev->flags&IFF_PROMISC) { /* Log any net taps. */ printk("%s: Promiscuous mode enabled.\n", dev->name); - lp->init_block.mode = 0x8000; + lp->init_block.mode |= 0x8000; } else { int num_addrs=dev->mc_count; if(dev->flags&IFF_ALLMULTI) num_addrs=1; /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */ memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter)); - lp->init_block.mode = 0x0000; + lp->init_block.mode &= ~0x8000; } outw(0, ioaddr+PCNET32_ADDR); diff -u --recursive --new-file v2.0.35/linux/drivers/net/plip.c linux/drivers/net/plip.c --- v2.0.35/linux/drivers/net/plip.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/plip.c Sun Nov 15 10:33:03 1998 @@ -390,7 +390,7 @@ return TIMEOUT; } c0 = inb(PAR_STATUS(dev)); - printk("%s: transmit timeout(%d,%02x)\n", + printk(KERN_WARNING "%s: transmit timeout(%d,%02x)\n", dev->name, snd->state, c0); } nl->enet_stats.tx_errors++; @@ -408,7 +408,7 @@ return TIMEOUT; } c0 = inb(PAR_STATUS(dev)); - printk("%s: receive timeout(%d,%02x)\n", + printk(KERN_WARNING "%s: receive timeout(%d,%02x)\n", dev->name, rcv->state, c0); } nl->enet_stats.rx_dropped++; @@ -538,13 +538,13 @@ return TIMEOUT; if (rcv->length.h > dev->mtu + dev->hard_header_len || rcv->length.h < 8) { - printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h); + printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h); return ERROR; } /* Malloc up new buffer. */ rcv->skb = dev_alloc_skb(rcv->length.h); if (rcv->skb == NULL) { - printk("%s: Memory squeeze.\n", dev->name); + printk(KERN_WARNING "%s: Memory squeeze.\n", dev->name); return ERROR; } skb_put(rcv->skb,rcv->length.h); @@ -668,7 +668,7 @@ unsigned int cx; if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) { - printk("%s: send skb lost\n", dev->name); + printk(KERN_ERR "%s: send skb lost\n", dev->name); snd->state = PLIP_PK_DONE; snd->skb = NULL; return ERROR; @@ -811,7 +811,7 @@ unsigned char c0; if (dev == NULL) { - printk ("plip_interrupt: irq %d for unknown device.\n", irq); + printk (KERN_ERR "plip_interrupt: irq %d for unknown device.\n", irq); return; } @@ -845,12 +845,12 @@ case PLIP_CN_RECEIVE: sti(); - printk("%s: receive interrupt when receiving packet\n", dev->name); + printk(KERN_WARNING "%s: receive interrupt when receiving packet\n", dev->name); break; case PLIP_CN_ERROR: sti(); - printk("%s: receive interrupt in error state\n", dev->name); + printk(KERN_WARNING "%s: receive interrupt in error state\n", dev->name); break; } } @@ -868,7 +868,7 @@ return nl->orig_rebuild_header(buff, dev, dst, skb); if (eth->h_proto != htons(ETH_P_IP)) { - printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto); + printk(KERN_WARNING "plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto); memcpy(eth->h_source, dev->dev_addr, dev->addr_len); return 0; } @@ -897,12 +897,12 @@ } if (set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); + printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name); return 1; } if (skb->len > dev->mtu + dev->hard_header_len) { - printk("%s: packet too big, %d.\n", dev->name, (int)skb->len); + printk(KERN_WARNING "%s: packet too big, %d.\n", dev->name, (int)skb->len); dev->tbusy = 0; dev_kfree_skb(skb, FREE_WRITE); return 0; @@ -940,13 +940,13 @@ int i; if (dev->irq == 0) { - printk("%s: IRQ is not set. Please set it by ifconfig.\n", dev->name); + printk(KERN_INFO "%s: IRQ is not set. Please set it by ifconfig.\n", dev->name); return -EAGAIN; } cli(); if (request_irq(dev->irq , plip_interrupt, 0, dev->name, NULL) != 0) { sti(); - printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq); + printk(KERN_WARNING "%s: couldn't get IRQ %d.\n", dev->name, dev->irq); return -EAGAIN; } irq2dev_map[dev->irq] = dev; @@ -1029,7 +1029,7 @@ if (map->base_addr != (unsigned long)-1 && map->base_addr != dev->base_addr) - printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name); + printk(KERN_WARNING "%s: You cannot change base_addr of this interface (ignored).\n", dev->name); if (map->irq != (unsigned char)-1) dev->irq = map->irq; diff -u --recursive --new-file v2.0.35/linux/drivers/net/rcif.h linux/drivers/net/rcif.h --- v2.0.35/linux/drivers/net/rcif.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/rcif.h Sun Nov 15 10:33:03 1998 @@ -0,0 +1,235 @@ +/* +** ************************************************************************* +** +** +** R C I F . H +** +** +** RedCreek InterFace include file. +** +** --------------------------------------------------------------------- +** --- Copyright (c) 1998, RedCreek Communications Inc. --- +** --- All rights reserved. --- +** --------------------------------------------------------------------- +** +** File Description: +** +** Header file private ioctl commands. +** +** +** 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 RCIF_H +#define RCIF_H + +/* The following protocol revision # should be incremented every time + a new protocol or new structures are used in this file. */ +int USER_PROTOCOL_REV = 1; /* used to track different protocol revisions */ + +/* define a single TCB & buffer */ +typedef struct /* a single buffer */ +{ + U32 context; /* context */ + U32 scount; /* segment count */ + U32 size; /* segment size */ + U32 addr; /* segment physical address */ +} +__attribute__((packed)) +singleB, *psingleB ; +typedef struct /* a single TCB */ +{ + /* + ** +-----------------------+ + ** | 1 | one buffer in the TCB + ** +-----------------------+ + ** | | user's buffer reference + ** +-----------------------+ + ** | 1 | one segment buffer + ** +-----------------------+ _ + ** | | size \ + ** +-----------------------+ \ segment descriptor + ** | | physical address of buffer / + ** +-----------------------+ _/ + */ + U32 bcount; /* buffer count */ + singleB b; /* buffer */ + +} +__attribute__((packed)) +singleTCB, *psingleTCB; + +/* + When adding new entries, please add all 5 related changes, since + it helps keep everything consistent: + 1) User structure entry + 2) User data entry + 3) Structure short-cut entry + 4) Data short-cut entry + 5) Command identifier entry + + For Example ("GETSPEED"): + 1) struct RCgetspeed_tag { U32 LinkSpeedCode; } RCgetspeed; + 2) struct RCgetspeed_tag *getspeed; + 3) #define RCUS_GETSPEED data.RCgetspeed; + 4) #define RCUD_GETSPEED _RC_user_data.getspeed + 5) #define RCUC_GETSPEED 0x02 + + Notes for the "GETSPEED" entry, above: + 1) RCgetspeed - RC{name} + RCgetspeed_tag - RC{name}_tag + LinkSpeedCode - create any structure format desired (not too large, + since memory will be unioned with all other entries) + 2) RCgetspeed_tag - RC{name}_tag chosen in #1 + getspeed - arbitrary name (ptr to structure in #1) + 3) RCUS_GETSPEED - RCUS_{NAME} ("NAME" & "name" do not have to the same) + data.RCgetspeed - data.RC{name} ("RC{name}" from #1) + 4) RCUD_GETSPEED - _RC_user_data.getspeed ("getspeed" from #2) + 5) RCUC_GETSPEED - unique hex identifier entry. +*/ + +typedef struct RC_user_tag RCuser_struct; + +/* 1) User structure entry */ +struct RC_user_tag +{ + int cmd; + union + { + /* GETINFO structure */ + struct RCgetinfo_tag { + unsigned long int mem_start; + unsigned long int mem_end; + unsigned long int base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; + } RCgetinfo; /* <---- RCgetinfo */ + + /* GETSPEED structure */ + struct RCgetspeed_tag { + U32 LinkSpeedCode; + } RCgetspeed; /* <---- RCgetspeed */ + + /* GETFIRMWAREVER structure */ + #define FirmStringLen 80 + struct RCgetfwver_tag { + U8 FirmString[FirmStringLen]; + } RCgetfwver; /* <---- RCgetfwver */ + + /* GETIPANDMASK structure */ + struct RCgetipnmask_tag { + U32 IpAddr; + U32 NetMask; + } RCgetipandmask; /* <---- RCgetipandmask */ + + /* GETMAC structure */ + #define MAC_SIZE 10 + struct RCgetmac_tag { + U8 mac[MAC_SIZE]; + } RCgetmac; /* <---- RCgetmac */ + + /* GETLINKSTATUS structure */ + struct RCgetlnkstatus_tag { + U32 ReturnStatus; + } RCgetlnkstatus; /* <---- RCgetlnkstatus */ + + /* GETLINKSTATISTICS structure */ + struct RCgetlinkstats_tag { + RCLINKSTATS StatsReturn; + } RCgetlinkstats; /* <---- RCgetlinkstats */ + + /* DEFAULT structure (when no command was recognized) */ + struct RCdefault_tag { + int rc; + } RCdefault; /* <---- RCdefault */ + + } data; + +}; /* struct RC_user_tag { ... } */ + +/* 2) User data entry */ +/* RCUD = RedCreek User Data */ +union RC_user_data_tag { /* structure tags used are taken from RC_user_tag structure above */ + struct RCgetinfo_tag *getinfo; + struct RCgetspeed_tag *getspeed; + struct RCgetfwver_tag *getfwver; + struct RCgetipnmask_tag *getipandmask; + struct RCgetmac_tag *getmac; + struct RCgetlnkstatus_tag *getlinkstatus; + struct RCgetlinkstats_tag *getlinkstatistics; + struct RCdefault_tag *rcdefault; +} _RC_user_data; /* declare as a global, so the defines below will work */ + +/* 3) Structure short-cut entry */ +/* define structure short-cuts */ /* structure names are taken from RC_user_tag structure above */ +#define RCUS_GETINFO data.RCgetinfo; +#define RCUS_GETSPEED data.RCgetspeed; +#define RCUS_GETFWVER data.RCgetfwver; +#define RCUS_GETIPANDMASK data.RCgetipandmask; +#define RCUS_GETMAC data.RCgetmac; +#define RCUS_GETLINKSTATUS data.RCgetlnkstatus; +#define RCUS_GETLINKSTATISTICS data.RCgetlinkstats; +#define RCUS_DEFAULT data.RCdefault; + +/* 4) Data short-cut entry */ +/* define data short-cuts */ /* pointer names are from RC_user_data_tag union (just below RC_user_tag) */ +#define RCUD_GETINFO _RC_user_data.getinfo +#define RCUD_GETSPEED _RC_user_data.getspeed +#define RCUD_GETFWVER _RC_user_data.getfwver +#define RCUD_GETIPANDMASK _RC_user_data.getipandmask +#define RCUD_GETMAC _RC_user_data.getmac +#define RCUD_GETLINKSTATUS _RC_user_data.getlinkstatus +#define RCUD_GETLINKSTATISTICS _RC_user_data.getlinkstatistics +#define RCUD_DEFAULT _RC_user_data.rcdefault + +/* 5) Command identifier entry */ +/* define command identifiers */ +#define RCUC_GETINFO 0x01 +#define RCUC_GETSPEED 0x02 +#define RCUC_GETFWVER 0x03 +#define RCUC_GETIPANDMASK 0x04 +#define RCUC_GETMAC 0x05 +#define RCUC_GETLINKSTATUS 0x06 +#define RCUC_GETLINKSTATISTICS 0x07 +#define RCUC_DEFAULT 0xff + +/* define ioctl commands to use, when talking to RC 45/PCI driver */ +#define RCU_PROTOCOL_REV SIOCDEVPRIVATE +#define RCU_COMMAND SIOCDEVPRIVATE+1 + +/* + Intended use for the above defines is shown below (GETINFO, as this example): + + RCuser_struct RCuser; // declare RCuser structure + struct ifreq ifr; // declare an interface request structure + + RCuser.cmd = RCUC_GETINFO; // set user command to GETINFO + ifr->ifr_data = (caddr_t) &RCuser; // set point to user structure + + sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); // get a socket + ioctl(sock, RCU_COMMAND, &ifr); // do ioctl on socket + + RCUD_GETINFO = &RCuser.RCUS_GETINFO; // set data pointer for GETINFO + + // print results + printf("memory 0x%lx-0x%lx, base address 0x%x, irq 0x%x\n", + RCUD_GETINFO->mem_start, RCUD_GETINFO->mem_end, + RCUD_GETINFO->base_addr, RCUD_GETINFO->irq); +*/ + +#endif /* RCIF_H */ + diff -u --recursive --new-file v2.0.35/linux/drivers/net/rcmtl.c linux/drivers/net/rcmtl.c --- v2.0.35/linux/drivers/net/rcmtl.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/rcmtl.c Sun Nov 15 10:33:03 1998 @@ -0,0 +1,2058 @@ +/* +** ************************************************************************* +** +** +** R C M T L . C $Revision: 1.1 $ +** +** +** RedCreek Message Transport Layer program module. +** +** --------------------------------------------------------------------- +** --- Copyright (c) 1997-1998, RedCreek Communications Inc. --- +** --- All rights reserved. --- +** --------------------------------------------------------------------- +** +** File Description: +** +** Host side message transport layer. +** +** 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 DEBUG + +#define RC_LINUX_MODULE +#include "rcmtl.h" + +#define dprintf kprintf + +extern int printk(const char * fmt, ...); + + /* RedCreek LAN device Target ID */ +#define LAN_TARGET_ID 0x10 + /* RedCreek's OSM default LAN receive Initiator */ +#define DEFAULT_RECV_INIT_CONTEXT 0xA17 + + +/* +** message structures +*/ + +#define TID_SZ 12 +#define FUNCTION_SZ 8 + +/* Transaction Reply Lists (TRL) Control Word structure */ + +#define TRL_SINGLE_FIXED_LENGTH 0x00 +#define TRL_SINGLE_VARIABLE_LENGTH 0x40 +#define TRL_MULTIPLE_FIXED_LENGTH 0x80 + +/* LAN Class specific functions */ + +#define LAN_PACKET_SEND 0x3B +#define LAN_SDU_SEND 0x3D +#define LAN_RECEIVE_POST 0x3E +#define LAN_RESET 0x35 +#define LAN_SHUTDOWN 0x37 + +/* Private Class specfic function */ +#define RC_PRIVATE 0xFF + +/* RC Executive Function Codes. */ + +#define RC_CMD_ADAPTER_ASSIGN 0xB3 +#define RC_CMD_ADAPTER_READ 0xB2 +#define RC_CMD_ADAPTER_RELEASE 0xB5 +#define RC_CMD_BIOS_INFO_SET 0xA5 +#define RC_CMD_BOOT_DEVICE_SET 0xA7 +#define RC_CMD_CONFIG_VALIDATE 0xBB +#define RC_CMD_CONN_SETUP 0xCA +#define RC_CMD_DEVICE_ASSIGN 0xB7 +#define RC_CMD_DEVICE_RELEASE 0xB9 +#define RC_CMD_HRT_GET 0xA8 +#define RC_CMD_ADAPTER_CLEAR 0xBE +#define RC_CMD_ADAPTER_CONNECT 0xC9 +#define RC_CMD_ADAPTER_RESET 0xBD +#define RC_CMD_LCT_NOTIFY 0xA2 +#define RC_CMD_OUTBOUND_INIT 0xA1 +#define RC_CMD_PATH_ENABLE 0xD3 +#define RC_CMD_PATH_QUIESCE 0xC5 +#define RC_CMD_PATH_RESET 0xD7 +#define RC_CMD_STATIC_MF_CREATE 0xDD +#define RC_CMD_STATIC_MF_RELEASE 0xDF +#define RC_CMD_STATUS_GET 0xA0 +#define RC_CMD_SW_DOWNLOAD 0xA9 +#define RC_CMD_SW_UPLOAD 0xAB +#define RC_CMD_SW_REMOVE 0xAD +#define RC_CMD_SYS_ENABLE 0xD1 +#define RC_CMD_SYS_MODIFY 0xC1 +#define RC_CMD_SYS_QUIESCE 0xC3 +#define RC_CMD_SYS_TAB_SET 0xA3 + + + /* Init Outbound Q status */ +#define RC_CMD_OUTBOUND_INIT_IN_PROGRESS 0x01 +#define RC_CMD_OUTBOUND_INIT_REJECTED 0x02 +#define RC_CMD_OUTBOUND_INIT_FAILED 0x03 +#define RC_CMD_OUTBOUND_INIT_COMPLETE 0x04 + + +#define UTIL_NOP 0x00 + + +/* RC Get Status State values */ + +#define ADAPTER_STATE_INITIALIZING 0x01 +#define ADAPTER_STATE_RESET 0x02 +#define ADAPTER_STATE_HOLD 0x04 +#define ADAPTER_STATE_READY 0x05 +#define ADAPTER_STATE_OPERATIONAL 0x08 +#define ADAPTER_STATE_FAILED 0x10 +#define ADAPTER_STATE_FAULTED 0x11 + + +/* Defines for Request Status Codes: Table 3-1 Reply Status Codes. */ + +#define RC_REPLY_STATUS_SUCCESS 0x00 +#define RC_REPLY_STATUS_ABORT_DIRTY 0x01 +#define RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02 +#define RC_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03 +#define RC_REPLY_STATUS_ERROR_DIRTY 0x04 +#define RC_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05 +#define RC_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06 +#define RC_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x07 +#define RC_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x08 +#define RC_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x09 +#define RC_REPLY_STATUS_TRANSACTION_ERROR 0x0A +#define RC_REPLY_STATUS_PROGRESS_REPORT 0x80 + + +/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/ + +#define RC_DS_SUCCESS 0x0000 +#define RC_DS_BAD_KEY 0x0001 +#define RC_DS_CHAIN_BUFFER_TOO_LARGE 0x0002 +#define RC_DS_DEVICE_BUSY 0x0003 +#define RC_DS_DEVICE_LOCKED 0x0004 +#define RC_DS_DEVICE_NOT_AVAILABLE 0x0005 +#define RC_DS_DEVICE_RESET 0x0006 +#define RC_DS_INAPPROPRIATE_FUNCTION 0x0007 +#define RC_DS_INSUFFICIENT_RESOURCE_HARD 0x0008 +#define RC_DS_INSUFFICIENT_RESOURCE_SOFT 0x0009 +#define RC_DS_INVALID_INITIATOR_ADDRESS 0x000A +#define RC_DS_INVALID_MESSAGE_FLAGS 0x000B +#define RC_DS_INVALID_OFFSET 0x000C +#define RC_DS_INVALID_PARAMETER 0x000D +#define RC_DS_INVALID_REQUEST 0x000E +#define RC_DS_INVALID_TARGET_ADDRESS 0x000F +#define RC_DS_MESSAGE_TOO_LARGE 0x0010 +#define RC_DS_MESSAGE_TOO_SMALL 0x0011 +#define RC_DS_MISSING_PARAMETER 0x0012 +#define RC_DS_NO_SUCH_PAGE 0x0013 +#define RC_DS_REPLY_BUFFER_FULL 0x0014 +#define RC_DS_TCL_ERROR 0x0015 +#define RC_DS_TIMEOUT 0x0016 +#define RC_DS_UNKNOWN_ERROR 0x0017 +#define RC_DS_UNKNOWN_FUNCTION 0x0018 +#define RC_DS_UNSUPPORTED_FUNCTION 0x0019 +#define RC_DS_UNSUPPORTED_VERSION 0x001A + + /* msg header defines for VersionOffset */ +#define RCMSGVER_1 0x0001 +#define SGL_OFFSET_0 RCMSGVER_1 +#define SGL_OFFSET_4 (0x0040 | RCMSGVER_1) +#define TRL_OFFSET_5 (0x0050 | RCMSGVER_1) +#define TRL_OFFSET_6 (0x0060 | RCMSGVER_1) + + /* msg header defines for MsgFlags */ +#define MSG_STATIC 0x0100 +#define MSG_64BIT_CNTXT 0x0200 +#define MSG_MULTI_TRANS 0x1000 +#define MSG_FAIL 0x2000 +#define MSG_LAST 0x4000 +#define MSG_REPLY 0x8000 + + /* normal LAN request message MsgFlags and VersionOffset (0x1041) */ +#define LAN_MSG_REQST (MSG_MULTI_TRANS | SGL_OFFSET_4) + + /* minimum size msg */ +#define THREE_WORD_MSG_SIZE 0x00030000 +#define FOUR_WORD_MSG_SIZE 0x00040000 +#define FIVE_WORD_MSG_SIZE 0x00050000 +#define SIX_WORD_MSG_SIZE 0x00060000 +#define SEVEN_WORD_MSG_SIZE 0x00070000 +#define EIGHT_WORD_MSG_SIZE 0x00080000 +#define NINE_WORD_MSG_SIZE 0x00090000 + +/* Special TID Assignments */ + +#define ADAPTER_TID 0 +#define HOST_TID 1 + + /* RedCreek private message codes */ +#define RC_PRIVATE_GET_MAC_ADDR 0x0001/**/ /* OBSOLETE */ +#define RC_PRIVATE_SET_MAC_ADDR 0x0002 +#define RC_PRIVATE_GET_LAN_STATS 0x0003 +#define RC_PRIVATE_GET_LINK_STATUS 0x0004 +#define RC_PRIVATE_SET_LINK_SPEED 0x0005 +#define RC_PRIVATE_SET_IP_AND_MASK 0x0006 +/* #define RC_PRIVATE_GET_IP_AND_MASK 0x0007 */ /* OBSOLETE */ +#define RC_PRIVATE_GET_LINK_SPEED 0x0008 +#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009 +/* #define RC_PRIVATE_GET_MAC_ADDR 0x000A *//**/ +#define RC_PRIVATE_GET_IP_AND_MASK 0x000B /**/ +#define RC_PRIVATE_DEBUG_MSG 0x000C +#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY 0x000D + +#define RC_PRIVATE_REBOOT 0x00FF + + +/* RC message header */ +typedef struct _RC_MSG_FRAME +{ + U8 VersionOffset; + U8 MsgFlags; + U16 MessageSize; + BF TargetAddress:TID_SZ; + BF InitiatorAddress:TID_SZ; + BF Function:FUNCTION_SZ; + U32 InitiatorContext; + /* SGL[] */ +} + RC_MSG_FRAME, *PRC_MSG_FRAME; + + + /* assumed a 16K minus 256 byte space for outbound queue message frames */ +#define MSG_FRAME_SIZE 512 +#define NMBR_MSG_FRAMES 30 + +/* +** Message Unit CSR definitions for RedCreek PCI45 board +*/ +typedef struct tag_rcatu +{ + volatile unsigned long APICRegSel; /* APIC Register Select */ + volatile unsigned long reserved0; + volatile unsigned long APICWinReg; /* APIC Window Register */ + volatile unsigned long reserved1; + volatile unsigned long InMsgReg0; /* inbound message register 0 */ + volatile unsigned long InMsgReg1; /* inbound message register 1 */ + volatile unsigned long OutMsgReg0; /* outbound message register 0 */ + volatile unsigned long OutMsgReg1; /* outbound message register 1 */ + volatile unsigned long InDoorReg; /* inbound doorbell register */ + volatile unsigned long InIntStat; /* inbound interrupt status register */ + volatile unsigned long InIntMask; /* inbound interrupt mask register */ + volatile unsigned long OutDoorReg; /* outbound doorbell register */ + volatile unsigned long OutIntStat; /* outbound interrupt status register */ + volatile unsigned long OutIntMask; /* outbound interrupt mask register */ + volatile unsigned long reserved2; + volatile unsigned long reserved3; + volatile unsigned long InQueue; /* inbound queue port */ + volatile unsigned long OutQueue; /* outbound queue port */ + volatile unsigned long reserved4; + volatile unsigned long reserver5; + /* RedCreek extension */ + volatile unsigned long EtherMacLow; + volatile unsigned long EtherMacHi; + volatile unsigned long IPaddr; + volatile unsigned long IPmask; +} + ATU, *PATU; + + /* + ** typedef PAB + ** + ** PCI Adapter Block - holds instance specific information and is located + ** in a reserved space at the start of the message buffer allocated by user. + */ +typedef struct +{ + PATU p_atu; /* ptr to ATU register block */ + PU8 pPci45LinBaseAddr; + PU8 pLinOutMsgBlock; + U32 outMsgBlockPhyAddr; + PFNTXCALLBACK pTransCallbackFunc; + PFNRXCALLBACK pRecvCallbackFunc; + PFNCALLBACK pRebootCallbackFunc; + PFNCALLBACK pCallbackFunc; + U16 ADAPTERState; + U16 InboundMFrameSize; +} + PAB, *PPAB; + + /* + ** in reserved space right after PAB in host memory is area for returning + ** values from card + */ + + /* + ** Array of pointers to PCI Adapter Blocks. + ** Indexed by a zero based (0-31) interface number. + */ +#define MAX_ADAPTERS 32 +static PPAB PCIAdapterBlock[MAX_ADAPTERS] = +{ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + + +/* +** typedef NICSTAT +** +** Data structure for NIC statistics retruned from PCI card. Data copied from +** here to user allocated RCLINKSTATS (see rclanmtl.h) structure. +*/ +typedef struct tag_NicStat +{ + unsigned long TX_good; + unsigned long TX_maxcol; + unsigned long TX_latecol; + unsigned long TX_urun; + unsigned long TX_crs; /* lost carrier sense */ + unsigned long TX_def; /* transmit deferred */ + unsigned long TX_singlecol; /* single collisions */ + unsigned long TX_multcol; + unsigned long TX_totcol; + unsigned long Rcv_good; + unsigned long Rcv_CRCerr; + unsigned long Rcv_alignerr; + unsigned long Rcv_reserr; /* rnr'd pkts */ + unsigned long Rcv_orun; + unsigned long Rcv_cdt; + unsigned long Rcv_runt; + unsigned long dump_status; /* last field directly from the chip */ +} + NICSTAT, *P_NICSTAT; + + +#define DUMP_DONE 0x0000A005 /* completed statistical dump */ +#define DUMP_CLEAR 0x0000A007 /* completed stat dump and clear counters */ + + +static volatile int msgFlag; + + +/* local function prototypes */ +static void ProcessOutboundAdapterMsg(PPAB pPab, U32 phyMsgAddr); +static int FillAdapterMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock); +static int GetAdapterStatus(PPAB pPab); +static int SendAdapterOutboundQInitMsg(PPAB pPab); +static int SendEnableSysMsg(PPAB pPab); + + + /* 1st 100h bytes of message block is reserved for messenger instance */ +#define ADAPTER_BLOCK_RESERVED_SPACE 0x100 + +/* +** ========================================================================= +** InitRCApiMsgLayer() +** +** Initialize the RedCreek API Module and adapter. +** +** Inputs: AdapterID - interface number from 0 to 15 +** pciBaseAddr - virual base address of PCI (set by BIOS) +** p_msgbuf - virual address to private message block (min. 16K) +** p_phymsgbuf - physical address of private message block +** TransmitCallbackFunction - address of transmit callback function +** ReceiveCallbackFunction - address of receive callback function +** +** private message block is allocated by user. It must be in locked pages. +** p_msgbuf and p_phymsgbuf point to the same location. Must be contigous +** memory block of a minimum of 16K byte and long word aligned. +** ========================================================================= +*/ +RC_RETURN +InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr, + PU8 p_msgbuf, PU8 p_phymsgbuf, + PFNTXCALLBACK TransmitCallbackFunction, + PFNRXCALLBACK ReceiveCallbackFunction, + PFNCALLBACK RebootCallbackFunction) +{ + int result; + PPAB pPab; + +#ifdef DEBUG + kprintf("InitAPI: Adapter:0x%04.4ux ATU:0x%08.8ulx msgbuf:0x%08.8ulx phymsgbuf:0x%08.8ulx\n" + "TransmitCallbackFunction:0x%08.8ulx ReceiveCallbackFunction:0x%08.8ulx\n", + AdapterID, pciBaseAddr, p_msgbuf, p_phymsgbuf, TransmitCallbackFunction, ReceiveCallbackFunction); +#endif /* DEBUG */ + + + /* Check if this interface already initialized - if so, shut it down */ + if (PCIAdapterBlock[AdapterID] != NULL) + { + printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID); +// RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL); + PCIAdapterBlock[AdapterID] = NULL; + } + + /* + ** store adapter instance values in adapter block. + ** Adapter block is at beginning of message buffer + */ + pPab = (PPAB)p_msgbuf; + + pPab->p_atu = (PATU)pciBaseAddr; + pPab->pPci45LinBaseAddr = (PU8)pciBaseAddr; + + /* Set outbound message frame addr - skip over Adapter Block */ + pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE); + pPab->pLinOutMsgBlock = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE); + + /* store callback function addresses */ + pPab->pTransCallbackFunc = TransmitCallbackFunction; + pPab->pRecvCallbackFunc = ReceiveCallbackFunction; + pPab->pRebootCallbackFunc = RebootCallbackFunction; + pPab->pCallbackFunc = (PFNCALLBACK)NULL; + + /* + ** Initialize API + */ + result = GetAdapterStatus(pPab); + + if (result != RC_RTN_NO_ERROR) + return result; + + if (pPab->ADAPTERState == ADAPTER_STATE_OPERATIONAL) + { + printk("pPab->ADAPTERState == op: resetting adapter\n"); + RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL); + } + + result = SendAdapterOutboundQInitMsg(pPab); + + if (result != RC_RTN_NO_ERROR) + return result; + + result = SendEnableSysMsg(pPab); + + if (result != RC_RTN_NO_ERROR) + return result; + + PCIAdapterBlock[AdapterID] = pPab; + return RC_RTN_NO_ERROR; +} + +/* +** ========================================================================= +** Disable and Enable Adapter interrupts. Adapter interrupts are enabled at Init time +** but can be disabled and re-enabled through these two function calls. +** Packets will still be put into any posted received buffers and packets will +** be sent through RCSendPacket() functions. Disabling Adapter interrupts +** will prevent hardware interrupt to host even though the outbound Adapter msg +** queue is not emtpy. +** ========================================================================= +*/ +#define i960_OUT_POST_Q_INT_BIT 0x0008 /* bit set masks interrupts */ + +RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID) +{ + PPAB pPab; + + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT; + + return RC_RTN_NO_ERROR; +} + +RC_RETURN RCEnableAdapterInterrupts(U16 AdapterID) +{ + PPAB pPab; + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT; + + return RC_RTN_NO_ERROR; + +} + + +/* +** ========================================================================= +** RCSendPacket() +** ========================================================================= +*/ +RC_RETURN +RCSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock) +{ + U32 msgOffset; + PU32 pMsg; + int size; + PPAB pPab; + +#ifdef DEBUG +kprintf("RCSendPacket()...\n"); +#endif /* DEBUG */ + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + /* get Inbound free Q entry - reading from In Q gets free Q entry */ + /* offset to Msg Frame in PCI msg block */ + + msgOffset = pPab->p_atu->InQueue; + + if (msgOffset == 0xFFFFFFFF) + { +#ifdef DEBUG + kprintf("RCSendPacket(): Inbound Free Q empty!\n"); +#endif /* DEBUG */ + return RC_RTN_FREE_Q_EMPTY; + } + + /* calc virual address of msg - virual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + + size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock); + + if (size == -1) /* error processing TCB - send NOP msg */ + { +#ifdef DEBUG + kprintf("RCSendPacket(): Error Rrocess TCB!\n"); +#endif /* DEBUG */ + pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID; + return RC_RTN_TCB_ERROR; + } + else /* send over msg header */ + { + pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */ + pMsg[1] = LAN_PACKET_SEND << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = InitiatorContext; + pMsg[3] = 0; /* batch reply */ + /* post to Inbound Post Q */ + pPab->p_atu->InQueue = msgOffset; + return RC_RTN_NO_ERROR; + } +} + + +/* +** ========================================================================= +** RCPostRecvBuffer() +** +** inputs: pBufrCntrlBlock - pointer to buffer control block +** +** returns TRUE if successful in sending message, else FALSE. +** ========================================================================= +*/ +RC_RETURN +RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock) +{ + U32 msgOffset; + PU32 pMsg; + int size; + PPAB pPab; + +#ifdef DEBUG +kprintf("RCPostRecvBuffers()...\n"); +#endif /* DEBUG */ + + /* search for DeviceHandle */ + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + + /* get Inbound free Q entry - reading from In Q gets free Q entry */ + /* offset to Msg Frame in PCI msg block */ + msgOffset = pPab->p_atu->InQueue; + + if (msgOffset == 0xFFFFFFFF) + { +#ifdef DEBUG + kprintf("RCPostRecvBuffers(): Inbound Free Q empty!\n"); +#endif /* DEBUG */ + return RC_RTN_FREE_Q_EMPTY; + + } + /* calc virual address of msg - virual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + + size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock); + + if (size == -1) /* error prcessing TCB - send 3 DWORD private msg == NOP */ + { +#ifdef DEBUG + kprintf("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", size); +#endif /* DEBUG */ + pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID; + /* post to Post Q */ + pPab->p_atu->InQueue = msgOffset; + return RC_RTN_TCB_ERROR; + } + else /* send over size msg header */ + { + pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */ + pMsg[1] = LAN_RECEIVE_POST << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; + pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */ + /* post to Post Q */ + pPab->p_atu->InQueue = msgOffset; + return RC_RTN_NO_ERROR; + } +} + + +/* +** ========================================================================= +** RCProcMsgQ() +** +** Process outbound message queue until empty. +** ========================================================================= +*/ +void +RCProcMsgQ(U16 AdapterID) +{ + U32 phyAddrMsg; + PU8 p8Msg; + PU32 p32; + U16 count; + PPAB pPab; + unsigned char debug_msg[20]; + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return; + + phyAddrMsg = pPab->p_atu->OutQueue; + + while (phyAddrMsg != 0xFFFFFFFF) + { + p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr); + p32 = (PU32)p8Msg; + + //printk(" msg: 0x%x 0x%x \n", p8Msg[7], p32[5]); + + /* + ** Send Packet Reply Msg + */ + if (LAN_PACKET_SEND == p8Msg[7]) /* function code byte */ + { + count = *(PU16)(p8Msg+2); + count -= p8Msg[0] >> 4; + /* status, count, context[], adapter */ + (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID); + } + /* + ** Receive Packet Reply Msg */ + else if (LAN_RECEIVE_POST == p8Msg[7]) + { +#ifdef DEBUG + kprintf("RECV_REPLY pPab:0x%08.8ulx p8Msg:0x%08.8ulx p32:0x%08.8ulx\n", pPab, p8Msg, p32); + kprintf("msg: 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", + p32[0], p32[1], p32[2], p32[3]); + kprintf(" 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", + p32[4], p32[5], p32[6], p32[7]); + kprintf(" 0x%08.8ulx:0X%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", + p32[8], p32[9], p32[10], p32[11]); +#endif + /* status, count, buckets remaining, packetParmBlock, adapter */ + (*pPab->pRecvCallbackFunc)(p8Msg[19], p8Msg[12], p32[5], p32+6, AdapterID); + + + } + else if (LAN_RESET == p8Msg[7] || LAN_SHUTDOWN == p8Msg[7]) + { + if (pPab->pCallbackFunc) + { + (*pPab->pCallbackFunc)(p8Msg[19],0,0,AdapterID); + } + else + { + pPab->pCallbackFunc = (PFNCALLBACK) 1; + } + //PCIAdapterBlock[AdapterID] = 0; + } + else if (RC_PRIVATE == p8Msg[7]) + { + //printk("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]); + switch (p32[5]) + { + case RC_PRIVATE_DEBUG_MSG: + msgFlag = 1; + /*printk("Received RC_PRIVATE msg\n");*/ + debug_msg[15] = (p32[6]&0xff000000) >> 24; + debug_msg[14] = (p32[6]&0x00ff0000) >> 16; + debug_msg[13] = (p32[6]&0x0000ff00) >> 8; + debug_msg[12] = (p32[6]&0x000000ff); + + debug_msg[11] = (p32[7]&0xff000000) >> 24; + debug_msg[10] = (p32[7]&0x00ff0000) >> 16; + debug_msg[ 9] = (p32[7]&0x0000ff00) >> 8; + debug_msg[ 8] = (p32[7]&0x000000ff); + + debug_msg[ 7] = (p32[8]&0xff000000) >> 24; + debug_msg[ 6] = (p32[8]&0x00ff0000) >> 16; + debug_msg[ 5] = (p32[8]&0x0000ff00) >> 8; + debug_msg[ 4] = (p32[8]&0x000000ff); + + debug_msg[ 3] = (p32[9]&0xff000000) >> 24; + debug_msg[ 2] = (p32[9]&0x00ff0000) >> 16; + debug_msg[ 1] = (p32[9]&0x0000ff00) >> 8; + debug_msg[ 0] = (p32[9]&0x000000ff); + + debug_msg[16] = '\0'; + printk (debug_msg); + break; + case RC_PRIVATE_REBOOT: + printk("Adapter reboot initiated...\n"); + if (pPab->pRebootCallbackFunc) + { + (*pPab->pRebootCallbackFunc)(0,0,0,AdapterID); + } + break; + default: + printk("Unknown private msg received: 0x%x\n", + p32[5]); + break; + } + } + + /* + ** Process other Msg's + */ + else + { + ProcessOutboundAdapterMsg(pPab, phyAddrMsg); + } + + /* return MFA to outbound free Q*/ + pPab->p_atu->OutQueue = phyAddrMsg; + + /* any more msgs? */ + phyAddrMsg = pPab->p_atu->OutQueue; + } +} + + +/* +** ========================================================================= +** Returns LAN interface statistical counters to space provided by caller at +** StatsReturnAddr. Returns 0 if success, else RC_RETURN code. +** This function will call the WaitCallback function provided by +** user while waiting for card to respond. +** ========================================================================= +*/ +RC_RETURN +RCGetLinkStatistics(U16 AdapterID, + P_RCLINKSTATS StatsReturnAddr, + PFNWAITCALLBACK WaitCallback) +{ + U32 msgOffset; + volatile U32 timeout; + volatile PU32 pMsg; + volatile PU32 p32, pReturnAddr; + P_NICSTAT pStats; + int i; + PPAB pPab; + +/*kprintf("Get82558Stats() StatsReturnAddr:0x%08.8ulx\n", StatsReturnAddr);*/ + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + msgOffset = pPab->p_atu->InQueue; + + if (msgOffset == 0xFFFFFFFF) + { + #ifdef DEBUG + kprintf("Get8255XStats(): Inbound Free Q empty!\n"); + #endif + return RC_RTN_FREE_Q_EMPTY; + } + + /* calc virual address of msg - virual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + +/*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/ +/*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/ + + pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; + pMsg[3] = 0x112; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LAN_STATS; + pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); + + p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB)); + + pStats = (P_NICSTAT)p32; + pStats->dump_status = 0xFFFFFFFF; + + /* post to Inbound Post Q */ + pPab->p_atu->InQueue = msgOffset; + + timeout = 100000; + while (1) + { + if (WaitCallback) + (*WaitCallback)(); + + for (i = 0; i < 1000; i++) + ; + + if (pStats->dump_status != 0xFFFFFFFF) + break; + + if (!timeout--) + { + #ifdef DEBUG + kprintf("RCGet82558Stats() Timeout waiting for NIC statistics\n"); + #endif + return RC_RTN_MSG_REPLY_TIMEOUT; + } + } + + pReturnAddr = (PU32)StatsReturnAddr; + + /* copy Nic stats to user's structure */ + for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++) + pReturnAddr[i] = p32[i]; + + return RC_RTN_NO_ERROR; +} + + +/* +** ========================================================================= +** Get82558LinkStatus() +** ========================================================================= +*/ +RC_RETURN +RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback) +{ + U32 msgOffset; + volatile U32 timeout; + volatile PU32 pMsg; + volatile PU32 p32; + PPAB pPab; + +/*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/ + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + msgOffset = pPab->p_atu->InQueue; + + if (msgOffset == 0xFFFFFFFF) + { + #ifdef DEBUG + dprintf("Get82558LinkStatus(): Inbound Free Q empty!\n"); + #endif + return RC_RTN_FREE_Q_EMPTY; + } + + /* calc virual address of msg - virual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); +/*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/ +/*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/ + + pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; + pMsg[3] = 0x112; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS; + pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); + + p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB)); + *p32 = 0xFFFFFFFF; + + /* post to Inbound Post Q */ + pPab->p_atu->InQueue = msgOffset; + + timeout = 100000; + while (1) + { + U32 i; + + if (WaitCallback) + (*WaitCallback)(); + + for (i = 0; i < 1000; i++) + ; + + if (*p32 != 0xFFFFFFFF) + break; + + if (!timeout--) + { + #ifdef DEBUG + kprintf("Timeout waiting for link status\n"); + #endif + return RC_RTN_MSG_REPLY_TIMEOUT; + } + } + + *ReturnAddr = *p32; /* 1 = up 0 = down */ + + return RC_RTN_NO_ERROR; + +} + +/* +** ========================================================================= +** RCGetMAC() +** +** get the MAC address the adapter is listening for in non-promiscous mode. +** MAC address is in media format. +** ========================================================================= +*/ +RC_RETURN +RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback) +{ + unsigned i, timeout; + U32 off; + PU32 p; + U32 temp[2]; + PPAB pPab; + PATU p_atu; + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + p_atu = pPab->p_atu; + + p_atu->EtherMacLow = 0; /* first zero return data */ + p_atu->EtherMacHi = 0; + + off = p_atu->InQueue; /* get addresss of message */ + + if (0xFFFFFFFF == off) + return RC_RTN_FREE_Q_EMPTY; + + p = (PU32)(pPab->pPci45LinBaseAddr + off); + +#ifdef RCDEBUG + printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", + (uint)p_atu, (uint)off, (uint)p); +#endif /* RCDEBUG */ + /* setup private message */ + p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; + p[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + p[2] = 0; /* initiator context */ + p[3] = 0x218; /* transaction context */ + p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR; + + + p_atu->InQueue = off; /* send it to the device */ +#ifdef RCDEBUG + printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", + (uint)p_atu, (uint)off, (uint)p); +#endif /* RCDEBUG */ + + /* wait for the rcpci45 board to update the info */ + timeout = 1000000; + while (0 == p_atu->EtherMacLow) + { + if (WaitCallback) + (*WaitCallback)(); + + for (i = 0; i < 1000; i++) + ; + + if (!timeout--) + { + printk("rc_getmac: Timeout\n"); + return RC_RTN_MSG_REPLY_TIMEOUT; + } + } + + /* read the mac address */ + temp[0] = p_atu->EtherMacLow; + temp[1] = p_atu->EtherMacHi; + memcpy((char *)mac, (char *)temp, 6); + + +#ifdef RCDEBUG +// printk("rc_getmac: 0x%X\n", ptr); +#endif /* RCDEBUG */ + + return RC_RTN_NO_ERROR; +} + + +/* +** ========================================================================= +** RCSetMAC() +** +** set MAC address the adapter is listening for in non-promiscous mode. +** MAC address is in media format. +** ========================================================================= +*/ +RC_RETURN +RCSetMAC(U16 AdapterID, PU8 mac) +{ + U32 off; + PU32 pMsg; + PPAB pPab; + + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + off = pPab->p_atu->InQueue; /* get addresss of message */ + + if (0xFFFFFFFF == off) + return RC_RTN_FREE_Q_EMPTY; + + pMsg = (PU32)(pPab->pPci45LinBaseAddr + off); + + /* setup private message */ + pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = 0; /* initiator context */ + pMsg[3] = 0x219; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR; + pMsg[5] = *(unsigned *)mac; /* first four bytes */ + pMsg[6] = *(unsigned *)(mac + 4); /* last two bytes */ + + pPab->p_atu->InQueue = off; /* send it to the device */ + + return RC_RTN_NO_ERROR ; +} + + +/* +** ========================================================================= +** RCSetLinkSpeed() +** +** set ethernet link speed. +** input: speedControl - determines action to take as follows +** 0 = reset and auto-negotiate (NWay) +** 1 = Full Duplex 100BaseT +** 2 = Half duplex 100BaseT +** 3 = Full Duplex 10BaseT +** 4 = Half duplex 10BaseT +** all other values are ignore (do nothing) +** ========================================================================= +*/ +RC_RETURN +RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode) +{ + U32 off; + PU32 pMsg; + PPAB pPab; + + + pPab =PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + off = pPab->p_atu->InQueue; /* get addresss of message */ + + if (0xFFFFFFFF == off) + return RC_RTN_FREE_Q_EMPTY; + + pMsg = (PU32)(pPab->pPci45LinBaseAddr + off); + + /* setup private message */ + pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = 0; /* initiator context */ + pMsg[3] = 0x219; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED; + pMsg[5] = LinkSpeedCode; /* link speed code */ + + pPab->p_atu->InQueue = off; /* send it to the device */ + + return RC_RTN_NO_ERROR ; +} + +/* +** ========================================================================= +** RCGetLinkSpeed() +** +** get ethernet link speed. +** +** 0 = Unknown +** 1 = Full Duplex 100BaseT +** 2 = Half duplex 100BaseT +** 3 = Full Duplex 10BaseT +** 4 = Half duplex 10BaseT +** +** ========================================================================= +*/ +RC_RETURN +RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback) +{ + U32 msgOffset, timeout; + PU32 pMsg; + volatile PU32 p32; + U8 AdapterLinkSpeed; + PPAB pPab; + + pPab =PCIAdapterBlock[AdapterID]; + + + msgOffset = pPab->p_atu->InQueue; + + + if (msgOffset == 0xFFFFFFFF) + { + kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n"); + return RC_RTN_FREE_Q_EMPTY; + } + + /* calc virtual address of msg - virtual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + + /* virtual pointer to return buffer - clear first two dwords */ + p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB)); + p32[0] = 0xff; + + /* setup private message */ + pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = 0; /* initiator context */ + pMsg[3] = 0x219; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED; + /* phys address to return status - area right after PAB */ + pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); + + /* post to Inbound Post Q */ + + pPab->p_atu->InQueue = msgOffset; + + /* wait for response */ + timeout = 1000000; + while(1) + { + int i; + + if (WaitCallback) + (*WaitCallback)(); + + for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */ + ; + + if (p32[0] != 0xff) + break; + + if (!timeout--) + { + kprintf("Timeout waiting for link speed from adapter\n"); + kprintf("0x%08.8ulx\n", p32[0]); + return RC_RTN_NO_LINK_SPEED; + } + } + + /* get Link speed */ + AdapterLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f; + + *pLinkSpeedCode= AdapterLinkSpeed; + + return RC_RTN_NO_ERROR; +} + +/* +** ========================================================================= +** RCReportDriverCapability(U16 AdapterID, U32 capability) +** +** Currently defined bits: +** WARM_REBOOT_CAPABLE 0x01 +** +** ========================================================================= +*/ +RC_RETURN +RCReportDriverCapability(U16 AdapterID, U32 capability) +{ + U32 off; + PU32 pMsg; + PPAB pPab; + + pPab =PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + off = pPab->p_atu->InQueue; /* get addresss of message */ + + if (0xFFFFFFFF == off) + return RC_RTN_FREE_Q_EMPTY; + + pMsg = (PU32)(pPab->pPci45LinBaseAddr + off); + + /* setup private message */ + pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = 0; /* initiator context */ + pMsg[3] = 0x219; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY; + pMsg[5] = capability; + + pPab->p_atu->InQueue = off; /* send it to the device */ + + return RC_RTN_NO_ERROR ; +} + +/* +** ========================================================================= +** RCGetFirmwareVer() +** +** Return firmware version in the form "SoftwareVersion : Bt BootVersion" +** +** ========================================================================= +*/ +RC_RETURN +RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback) +{ + U32 msgOffset, timeout; + PU32 pMsg; + volatile PU32 p32; + PPAB pPab; + + pPab =PCIAdapterBlock[AdapterID]; + + msgOffset = pPab->p_atu->InQueue; + + + if (msgOffset == 0xFFFFFFFF) + { + kprintf("RCGetFirmwareVer(): Inbound Free Q empty!\n"); + return RC_RTN_FREE_Q_EMPTY; + } + + /* calc virtual address of msg - virtual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + + /* virtual pointer to return buffer - clear first two dwords */ + p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB)); + p32[0] = 0xff; + + /* setup private message */ + pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = 0; /* initiator context */ + pMsg[3] = 0x219; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV; + /* phys address to return status - area right after PAB */ + pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); + + + + /* post to Inbound Post Q */ + + pPab->p_atu->InQueue = msgOffset; + + + /* wait for response */ + timeout = 1000000; + while(1) + { + int i; + + if (WaitCallback) + (*WaitCallback)(); + + for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */ + ; + + if (p32[0] != 0xff) + break; + + if (!timeout--) + { + kprintf("Timeout waiting for link speed from adapter\n"); + return RC_RTN_NO_FIRM_VER; + } + } + + strcpy(pFirmString, (PU8)p32); + return RC_RTN_NO_ERROR; +} + +/* +** ========================================================================= +** RCResetLANCard() +** +** ResourceFlags indicates whether to return buffer resource explicitly +** to host or keep and reuse. +** CallbackFunction (if not NULL) is the function to be called when +** reset is complete. +** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when +** reset is done (if not NULL). +** +** ========================================================================= +*/ +RC_RETURN +RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction) +{ + unsigned long off; + unsigned long *pMsg; + PPAB pPab; + int i; + long timeout = 0; + + + pPab =PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + off = pPab->p_atu->InQueue; /* get addresss of message */ + + if (0xFFFFFFFF == off) + return RC_RTN_FREE_Q_EMPTY; + + pPab->pCallbackFunc = CallbackFunction; + + pMsg = (PU32)(pPab->pPci45LinBaseAddr + off); + + /* setup message */ + pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = LAN_RESET << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; + pMsg[3] = ResourceFlags << 16; /* resource flags */ + + pPab->p_atu->InQueue = off; /* send it to the device */ + + if (CallbackFunction == (PFNCALLBACK)NULL) + { + /* call RCProcMsgQ() until something in pPab->pCallbackFunc + or until timer goes off */ + while (pPab->pCallbackFunc == (PFNCALLBACK)NULL) + { + RCProcMsgQ(AdapterID); + for (i = 0; i < 100000; i++) /* please don't hog the bus!!! */ + ; + timeout++; + if (timeout > 10000) + { + break; + } + } + if (ReturnAddr != (PU32)NULL) + *ReturnAddr = (U32)pPab->pCallbackFunc; + } + + return RC_RTN_NO_ERROR ; +} +/* +** ========================================================================= +** RCResetAdapter() +** +** Send StatusGet Msg, wait for results return directly to buffer. +** +** ========================================================================= +*/ +RC_RETURN +RCResetAdapter(U16 AdapterID) +{ + U32 msgOffset, timeout; + PU32 pMsg; + PPAB pPab; + volatile PU32 p32; + + pPab = PCIAdapterBlock[AdapterID]; + msgOffset = pPab->p_atu->InQueue; + + if (msgOffset == 0xFFFFFFFF) + { + return RC_RTN_FREE_Q_EMPTY; + } + + /* calc virtual address of msg - virtual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + + pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 | ADAPTER_TID; + pMsg[2] = 0; /* universal context */ + pMsg[3] = 0; /* universal context */ + pMsg[4] = 0; /* universal context */ + pMsg[5] = 0; /* universal context */ + /* phys address to return status - area right after PAB */ + pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); + pMsg[7] = 0; + pMsg[8] = 1; /* return 1 byte */ + + /* virual pointer to return buffer - clear first two dwords */ + p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB)); + p32[0] = 0; + p32[1] = 0; + + /* post to Inbound Post Q */ + + pPab->p_atu->InQueue = msgOffset; + + /* wait for response */ + timeout = 1000000; + while(1) + { + int i; + + for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */ + ; + + if (p32[0] || p32[1]) + break; + + if (!timeout--) + { + printk("RCResetAdapter timeout\n"); + return RC_RTN_MSG_REPLY_TIMEOUT; + } + } + return RC_RTN_NO_ERROR; +} + +/* +** ========================================================================= +** RCShutdownLANCard() +** +** ResourceFlags indicates whether to return buffer resource explicitly +** to host or keep and reuse. +** CallbackFunction (if not NULL) is the function to be called when +** shutdown is complete. +** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when +** shutdown is done (if not NULL). +** +** ========================================================================= +*/ +RC_RETURN +RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction) +{ + volatile PU32 pMsg; + U32 off; + PPAB pPab; + int i; + long timeout = 0; + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + off = pPab->p_atu->InQueue; /* get addresss of message */ + + if (0xFFFFFFFF == off) + return RC_RTN_FREE_Q_EMPTY; + + pPab->pCallbackFunc = CallbackFunction; + + pMsg = (PU32)(pPab->pPci45LinBaseAddr + off); + + /* setup message */ + pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = LAN_SHUTDOWN << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; + pMsg[3] = ResourceFlags << 16; /* resource flags */ + + pPab->p_atu->InQueue = off; /* send it to the device */ + + if (CallbackFunction == (PFNCALLBACK)NULL) + { + /* call RCProcMsgQ() until something in pPab->pCallbackFunc + or until timer goes off */ + while (pPab->pCallbackFunc == (PFNCALLBACK)NULL) + { + RCProcMsgQ(AdapterID); + for (i = 0; i < 100000; i++) /* please don't hog the bus!!! */ + ; + timeout++; + if (timeout > 10000) + { + break; + } + } + if (ReturnAddr != (PU32)NULL) + *ReturnAddr = (U32)pPab->pCallbackFunc; + } + return RC_RTN_NO_ERROR ; +} + + +/* +** ========================================================================= +** RCSetRavlinIPandMask() +** +** Set the Ravlin 45/PCI cards IP address and network mask. +** +** IP address and mask must be in network byte order. +** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be +** 0x04030201 and 0x00FFFFFF on a little endian machine. +** +** ========================================================================= +*/ +RC_RETURN +RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask) +{ + volatile PU32 pMsg; + U32 off; + PPAB pPab; + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + off = pPab->p_atu->InQueue; /* get addresss of message */ + + if (0xFFFFFFFF == off) + return RC_RTN_FREE_Q_EMPTY; + + pMsg = (PU32)(pPab->pPci45LinBaseAddr + off); + + /* setup private message */ + pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = 0; /* initiator context */ + pMsg[3] = 0x219; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK; + pMsg[5] = ipAddr; + pMsg[6] = netMask; + + + pPab->p_atu->InQueue = off; /* send it to the device */ + return RC_RTN_NO_ERROR ; + +} + +/* +** ========================================================================= +** RCGetRavlinIPandMask() +** +** get the IP address and MASK from the card +** +** ========================================================================= +*/ +RC_RETURN +RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, + PFNWAITCALLBACK WaitCallback) +{ + unsigned i, timeout; + U32 off; + PU32 pMsg, p32; + PPAB pPab; + PATU p_atu; + +#ifdef DEBUG + kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr); +#endif /* DEBUG */ + + pPab = PCIAdapterBlock[AdapterID]; + + if (pPab == NULL) + return RC_RTN_ADPTR_NOT_REGISTERED; + + p_atu = pPab->p_atu; + off = p_atu->InQueue; /* get addresss of message */ + + if (0xFFFFFFFF == off) + return RC_RTN_FREE_Q_EMPTY; + + p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB)); + *p32 = 0xFFFFFFFF; + + pMsg = (PU32)(pPab->pPci45LinBaseAddr + off); + +#ifdef DEBUG + kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32); +#endif /* DEBUG */ + /* setup private message */ + pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID; + pMsg[2] = 0; /* initiator context */ + pMsg[3] = 0x218; /* transaction context */ + pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK; + pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); + + p_atu->InQueue = off; /* send it to the device */ +#ifdef DEBUG + kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32); +#endif /* DEBUG */ + + /* wait for the rcpci45 board to update the info */ + timeout = 100000; + while (0xffffffff == *p32) + { + if (WaitCallback) + (*WaitCallback)(); + + for (i = 0; i < 1000; i++) + ; + + if (!timeout--) + { + #ifdef DEBUG + kprintf("RCGetRavlinIPandMask: Timeout\n"); + #endif /* DEBUG */ + return RC_RTN_MSG_REPLY_TIMEOUT; + } + } + +#ifdef DEBUG + kprintf("RCGetRavlinIPandMask: after time out\n", \ + "p32[0] (IpAddr) 0x%08.8ulx, p32[1] (IPmask) 0x%08.8ulx\n", p32[0], p32[1]); +#endif /* DEBUG */ + + /* send IP and mask to user's space */ + *pIpAddr = p32[0]; + *pNetMask = p32[1]; + + +#ifdef DEBUG + kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr); +#endif /* DEBUG */ + + return RC_RTN_NO_ERROR; +} + +/* +** ///////////////////////////////////////////////////////////////////////// +** ///////////////////////////////////////////////////////////////////////// +** +** local functions +** +** ///////////////////////////////////////////////////////////////////////// +** ///////////////////////////////////////////////////////////////////////// +*/ + +/* +** ========================================================================= +** SendAdapterOutboundQInitMsg() +** +** ========================================================================= +*/ +static int +SendAdapterOutboundQInitMsg(PPAB pPab) +{ + U32 msgOffset, timeout, phyOutQFrames, i; + volatile PU32 pMsg; + volatile PU32 p32; + + + + msgOffset = pPab->p_atu->InQueue; + + + if (msgOffset == 0xFFFFFFFF) + { +#ifdef DEBUG + kprintf("SendAdapterOutboundQInitMsg(): Inbound Free Q empty!\n"); +#endif /* DEBUG */ + return RC_RTN_FREE_Q_EMPTY; + } + + + /* calc virual address of msg - virual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + +#ifdef DEBUG +kprintf("SendAdapterOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset); +#endif /* DEBUG */ + + pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6; + pMsg[1] = RC_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID; + pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; + pMsg[3] = 0x106; /* transaction context */ + pMsg[4] = 4096; /* Host page frame size */ + pMsg[5] = MSG_FRAME_SIZE << 16 | 0x80; /* outbound msg frame size and Initcode */ + pMsg[6] = 0xD0000004; /* simple sgl element LE, EOB */ + /* phys address to return status - area right after PAB */ + pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); + + /* virual pointer to return buffer - clear first two dwords */ + p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB)); + p32[0] = 0; + + /* post to Inbound Post Q */ + pPab->p_atu->InQueue = msgOffset; + + /* wait for response */ + timeout = 100000; + while(1) + { + for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */ + ; + + if (p32[0]) + break; + + if (!timeout--) + { +#ifdef DEBUG + kprintf("Timeout wait for InitOutQ InPrgress status from adapter\n"); +#endif /* DEBUG */ + return RC_RTN_NO_STATUS; + } + } + + timeout = 100000; + while(1) + { + for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */ + ; + + if (p32[0] == RC_CMD_OUTBOUND_INIT_COMPLETE) + break; + + if (!timeout--) + { +#ifdef DEBUG + kprintf("Timeout wait for InitOutQ Complete status from adapter\n"); +#endif /* DEBUG */ + return RC_RTN_NO_STATUS; + } + } + + /* load PCI outbound free Q with MF physical addresses */ + phyOutQFrames = pPab->outMsgBlockPhyAddr; + + for (i = 0; i < NMBR_MSG_FRAMES; i++) + { + pPab->p_atu->OutQueue = phyOutQFrames; + phyOutQFrames += MSG_FRAME_SIZE; + } + return RC_RTN_NO_ERROR; +} + + +/* +** ========================================================================= +** GetAdapterStatus() +** +** Send StatusGet Msg, wait for results return directly to buffer. +** +** ========================================================================= +*/ +static int +GetAdapterStatus(PPAB pPab) +{ + U32 msgOffset, timeout; + PU32 pMsg; + volatile PU32 p32; + + + msgOffset = pPab->p_atu->InQueue; + printk("GetAdapterStatus: msg offset = 0x%x\n", msgOffset); + if (msgOffset == 0xFFFFFFFF) + { +#ifdef DEBUG + kprintf("GetAdapterStatus(): Inbound Free Q empty!\n"); +#endif /* DEBUG */ + return RC_RTN_FREE_Q_EMPTY; + } + + /* calc virual address of msg - virual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + + pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_CMD_STATUS_GET << 24 | HOST_TID << 12 | ADAPTER_TID; + pMsg[2] = 0; /* universal context */ + pMsg[3] = 0; /* universal context */ + pMsg[4] = 0; /* universal context */ + pMsg[5] = 0; /* universal context */ + /* phys address to return status - area right after PAB */ + pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); + pMsg[7] = 0; + pMsg[8] = 88; /* return 88 bytes */ + + /* virual pointer to return buffer - clear first two dwords */ + p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB)); + p32[0] = 0; + p32[1] = 0; + +#ifdef DEBUG +kprintf("GetAdapterStatus - pMsg:0x%08.8ulx, msgOffset:0x%08.8ulx, [1]:0x%08.8ulx, [6]:0x%08.8ulx\n", + pMsg, msgOffset, pMsg[1], pMsg[6]); +#endif /* DEBUG */ + + /* post to Inbound Post Q */ + pPab->p_atu->InQueue = msgOffset; + +#ifdef DEBUG +kprintf("Return status to p32 = 0x%08.8ulx\n", p32); +#endif /* DEBUG */ + + /* wait for response */ + timeout = 1000000; + while(1) + { + int i; + + for (i = 0; i < 1000; i++) /* please don't hog the bus!!! */ + ; + + if (p32[0] && p32[1]) + break; + + if (!timeout--) + { +#ifdef DEBUG + kprintf("Timeout waiting for status from adapter\n"); +kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]); +kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]); +kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]); +#endif /* DEBUG */ + return RC_RTN_NO_STATUS; + } + } + +#ifdef DEBUG +kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]); +kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]); +kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]); +#endif /* DEBUG */ + /* get adapter state */ + pPab->ADAPTERState = ((volatile PU8)p32)[10]; + pPab->InboundMFrameSize = ((volatile PU16)p32)[6]; + +#ifdef DEBUG + kprintf("adapter state 0x%02.2x InFrameSize = 0x%04.4x\n", + pPab->ADAPTERState, pPab->InboundMFrameSize); +#endif /* DEBUG */ + return RC_RTN_NO_ERROR; +} + + +/* +** ========================================================================= +** SendEnableSysMsg() +** +** +** ========================================================================= +*/ +static int +SendEnableSysMsg(PPAB pPab) +{ + U32 msgOffset; // timeout; + volatile PU32 pMsg; + + msgOffset = pPab->p_atu->InQueue; + + if (msgOffset == 0xFFFFFFFF) + { +#ifdef DEBUG + kprintf("SendEnableSysMsg(): Inbound Free Q empty!\n"); +#endif /* DEBUG */ + return RC_RTN_FREE_Q_EMPTY; + } + + /* calc virual address of msg - virual already mapped to physical */ + pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset); + +#ifdef DEBUG +kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset); +#endif /* DEBUG */ + + pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0; + pMsg[1] = RC_CMD_SYS_ENABLE << 24 | HOST_TID << 12 | ADAPTER_TID; + pMsg[2] = DEFAULT_RECV_INIT_CONTEXT; + pMsg[3] = 0x110; /* transaction context */ + pMsg[4] = 0x50657465; /* RedCreek Private */ + + /* post to Inbound Post Q */ + pPab->p_atu->InQueue = msgOffset; + + return RC_RTN_NO_ERROR; +} + + +/* +** ========================================================================= +** FillI12OMsgFromTCB() +** +** inputs pMsgU32 - virual pointer (mapped to physical) of message frame +** pXmitCntrlBlock - pointer to caller buffer control block. +** +** fills in LAN SGL after Transaction Control Word or Bucket Count. +** ========================================================================= +*/ +static int +FillAdapterMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock) +{ + unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags; + PU32 pTCB, pMsg; + + /* SGL element flags */ +#define EOB 0x40000000 +#define LE 0x80000000 +#define SIMPLE_SGL 0x10000000 +#define BC_PRESENT 0x01000000 + + pTCB = (PU32)pTransCtrlBlock; + pMsg = pMsgFrame; + nmbrDwords = 0; + +#ifdef DEBUG + kprintf("FillAdapterMsgSGLFromTCBX\n"); +kprintf("TCB 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", + pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]); +kprintf("pTCB 0x%08.8ulx, pMsg 0x%08.8ulx\n", pTCB, pMsg); +#endif /* DEBUG */ + + nmbrBuffers = *pTCB++; + + if (!nmbrBuffers) + { + return -1; + } + + do + { + context = *pTCB++; /* buffer tag (context) */ + nmbrSeg = *pTCB++; /* number of segments */ + + if (!nmbrSeg) + { + return -1; + } + + flags = SIMPLE_SGL | BC_PRESENT; + + if (1 == nmbrSeg) + { + flags |= EOB; + + if (1 == nmbrBuffers) + flags |= LE; + } + + /* 1st SGL buffer element has context */ + pMsg[0] = pTCB[0] | flags ; /* send over count (segment size) */ + pMsg[1] = context; + pMsg[2] = pTCB[1]; /* send buffer segment physical address */ + nmbrDwords += 3; + pMsg += 3; + pTCB += 2; + + + if (--nmbrSeg) + { + do + { + flags = SIMPLE_SGL; + + if (1 == nmbrSeg) + { + flags |= EOB; + + if (1 == nmbrBuffers) + flags |= LE; + } + + pMsg[0] = pTCB[0] | flags; /* send over count */ + pMsg[1] = pTCB[1]; /* send buffer segment physical address */ + nmbrDwords += 2; + pTCB += 2; + pMsg += 2; + + } while (--nmbrSeg); + } + + } while (--nmbrBuffers); + + return nmbrDwords; +} + + +/* +** ========================================================================= +** ProcessOutboundAdapterMsg() +** +** process reply message +** * change to msg structure * +** ========================================================================= +*/ +static void +ProcessOutboundAdapterMsg(PPAB pPab, U32 phyAddrMsg) +{ + PU8 p8Msg; + PU32 p32; + // U16 count; + + + p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr); + p32 = (PU32)p8Msg; + +#ifdef DEBUG + kprintf("VXD: ProcessOutboundAdapterMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg); + kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]); + kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]); +#endif /* DEBUG */ + + if (p32[4] >> 24 != RC_REPLY_STATUS_SUCCESS) + { +#ifdef DEBUG + kprintf("Message reply status not success\n"); +#endif /* DEBUG */ + return; + } + + switch (p8Msg[7] ) /* function code byte */ + { + case RC_CMD_SYS_TAB_SET: + msgFlag = 1; +#ifdef DEBUG + kprintf("Received RC_CMD_SYS_TAB_SET reply\n"); +#endif /* DEBUG */ + break; + + case RC_CMD_HRT_GET: + msgFlag = 1; +#ifdef DEBUG + kprintf("Received RC_CMD_HRT_GET reply\n"); +#endif /* DEBUG */ + break; + + case RC_CMD_LCT_NOTIFY: + msgFlag = 1; +#ifdef DEBUG + kprintf("Received RC_CMD_LCT_NOTIFY reply\n"); +#endif /* DEBUG */ + break; + + case RC_CMD_SYS_ENABLE: + msgFlag = 1; +#ifdef DEBUG + kprintf("Received RC_CMD_SYS_ENABLE reply\n"); +#endif /* DEBUG */ + break; + + default: +#ifdef DEBUG + kprintf("Received UNKNOWN reply\n"); +#endif /* DEBUG */ + break; + } +} diff -u --recursive --new-file v2.0.35/linux/drivers/net/rcmtl.h linux/drivers/net/rcmtl.h --- v2.0.35/linux/drivers/net/rcmtl.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/rcmtl.h Sun Nov 15 10:33:03 1998 @@ -0,0 +1,580 @@ +/* +** ************************************************************************* +** +** +** R C M T L . H $Revision: 3 $ +** +** +** RedCreek Message Transport Layer header file. +** +** --------------------------------------------------------------------- +** --- Copyright (c) 1997-1998, RedCreek Communications Inc. --- +** --- All rights reserved. --- +** --------------------------------------------------------------------- +** +** File Description: +** +** Header file for host message transport layer API and data types. +** +** 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 RCMTL_H +#define RCMTL_H + +/* Linux specific includes */ +#define kprintf printk +#ifdef RC_LINUX_MODULE /* linux modules need non-library version of string functions */ +#include +#else +#include +#endif + +/* PCI/45 Configuration space values */ +#define RC_PCI45_VENDOR_ID 0x4916 +#define RC_PCI45_DEVICE_ID 0x1960 + + + /* RedCreek API function return values */ +#define RC_RTN_NO_ERROR 0 +#define RC_RTN_NOT_INIT 1 +#define RC_RTN_FREE_Q_EMPTY 2 +#define RC_RTN_TCB_ERROR 3 +#define RC_RTN_TRANSACTION_ERROR 4 +#define RC_RTN_ADAPTER_ALREADY_INIT 5 +#define RC_RTN_MALLOC_ERROR 6 +#define RC_RTN_ADPTR_NOT_REGISTERED 7 +#define RC_RTN_MSG_REPLY_TIMEOUT 8 +#define RC_RTN_NO_STATUS 9 +#define RC_RTN_NO_FIRM_VER 10 +#define RC_RTN_NO_LINK_SPEED 11 + +/* Driver capability flags */ +#define WARM_REBOOT_CAPABLE 0x01 + + /* scalar data types */ +typedef unsigned char U8; +typedef unsigned char* PU8; +typedef unsigned short U16; +typedef unsigned short* PU16; +typedef unsigned long U32; +typedef unsigned long* PU32; +typedef unsigned long BF; +typedef int RC_RETURN; + + + /* + ** type PFNWAITCALLBACK + ** + ** pointer to void function - type used for WaitCallback in some functions + */ +typedef void (*PFNWAITCALLBACK)(void); /* void argument avoids compiler complaint */ + + /* + ** type PFNTXCALLBACK + ** + ** Pointer to user's transmit callback function. This user function is + ** called from RCProcMsgQ() when packet have been transmitted from buffers + ** given in the RCSendPacket() function. BufferContext is a pointer to + ** an array of 32 bit context values. These are the values the user assigned + ** and passed in the TCB to the RCSendPacket() function. PcktCount + ** indicates the number of buffer context values in the BufferContext[] array. + ** The User's TransmitCallbackFunction should recover (put back in free queue) + ** the packet buffers associated with the buffer context values. + */ +typedef void (*PFNTXCALLBACK)(U32 Status, + U16 PcktCount, + PU32 BufferContext, + U16 AdaterID); + + /* + ** type PFNRXCALLBACK + ** + ** Pointer to user's receive callback function. This user function + ** is called from RCProcMsgQ() when packets have been received into + ** previously posted packet buffers throught the RCPostRecvBuffers() function. + ** The received callback function should process the Packet Descriptor Block + ** pointed to by PacketDescBlock. See Packet Decription Block below. + */ +typedef void (*PFNRXCALLBACK)(U32 Status, + U8 PktCount, + U32 BucketsRemain, + PU32 PacketDescBlock, + U16 AdapterID); + + /* + ** type PFNCALLBACK + ** + ** Pointer to user's generic callback function. This user function + ** can be passed to LANReset or LANShutdown and is called when the + ** the reset or shutdown is complete. + ** Param1 and Param2 are invalid for LANReset and LANShutdown. + */ +typedef void (*PFNCALLBACK)(U32 Status, + U32 Param1, + U32 Param2, + U16 AdapterID); + +/* +** Status - Transmit and Receive callback status word +** +** A 32 bit Status is returned to the TX and RX callback functions. This value +** contains both the reply status and the detailed status as follows: +** +** 32 24 16 0 +** +------+------+------------+ +** | Reply| | Detailed | +** |Status| 0 | Status | +** +------+------+------------+ +** +** Reply Status and Detailed Status of zero indicates No Errors. +*/ + /* reply message status defines */ +#define RC_REPLY_STATUS_SUCCESS 0x00 +#define RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02 +#define RC_REPLY_STATUS_TRANSACTION_ERROR 0x0A + + +/* DetailedStatusCode defines */ +#define RC_DSC_SUCCESS 0x0000 +#define RC_DSC_DEVICE_FAILURE 0x0001 +#define RC_DSC_DESTINATION_NOT_FOUND 0x0002 +#define RC_DSC_TRANSMIT_ERROR 0x0003 +#define RC_DSC_TRANSMIT_ABORTED 0x0004 +#define RC_DSC_RECEIVE_ERROR 0x0005 +#define RC_DSC_RECEIVE_ABORTED 0x0006 +#define RC_DSC_DMA_ERROR 0x0007 +#define RC_DSC_BAD_PACKET_DETECTED 0x0008 +#define RC_DSC_OUT_OF_MEMORY 0x0009 +#define RC_DSC_BUCKET_OVERRUN 0x000A +#define RC_DSC_IOP_INTERNAL_ERROR 0x000B +#define RC_DSC_CANCELED 0x000C +#define RC_DSC_INVALID_TRANSACTION_CONTEXT 0x000D +#define RC_DSC_DESTINATION_ADDRESS_DETECTED 0x000E +#define RC_DSC_DESTINATION_ADDRESS_OMITTED 0x000F +#define RC_DSC_PARTIAL_PACKET_RETURNED 0x0010 + + +/* +** Packet Description Block (Received packets) +** +** A pointer to this block structure is returned to the ReceiveCallback +** function. It contains the list of packet buffers which have either been +** filled with a packet or returned to host due to a LANReset function. +** Currently there will only be one packet per receive bucket (buffer) posted. +** +** 32 24 0 +** +-----------------------+ -\ +** | Buffer 1 Context | \ +** +-----------------------+ \ +** | 0xC0000000 | / First Bucket Descriptor +** +-----+-----------------+ / +** | 0 | packet 1 length | / +** +-----------------------+ -\ +** | Buffer 2 Context | \ +** +-----------------------+ \ +** | 0xC0000000 | / Second Bucket Descriptor +** +-----+-----------------+ / +** | 0 | packet 2 length | / +** +-----+-----------------+ - +** | ... | ----- more bucket descriptors +** +-----------------------+ -\ +** | Buffer n Context | \ +** +-----------------------+ \ +** | 0xC0000000 | / Last Bucket Descriptor +** +-----+-----------------+ / +** | 0 | packet n length | / +** +-----+-----------------+ - +** +** Buffer Context values are those given to adapter in the TCB on calls to +** RCPostRecvBuffers(). +** +*/ + + + +/* +** Transaction Control Block (TCB) structure +** +** A structure like this is filled in by the user and passed by reference to +** RCSendPacket() and RCPostRecvBuffers() functions. Minimum size is five +** 32-bit words for one buffer with one segment descriptor. +** MAX_NMBR_POST_BUFFERS_PER_MSG defines the maximum single segment buffers +** that can be described in a given TCB. +** +** 32 0 +** +-----------------------+ +** | Buffer Count | Number of buffers in the TCB +** +-----------------------+ +** | Buffer 1 Context | first buffer reference +** +-----------------------+ +** | Buffer 1 Seg Count | number of segments in buffer +** +-----------------------+ +** | Buffer 1 Seg Desc 1 | first segment descriptor (size, physical address) +** +-----------------------+ +** | ... | more segment descriptors (size, physical address) +** +-----------------------+ +** | Buffer 1 Seg Desc n | last segment descriptor (size, physical address) +** +-----------------------+ +** | Buffer 2 Context | second buffer reference +** +-----------------------+ +** | Buffer 2 Seg Count | number of segments in buffer +** +-----------------------+ +** | Buffer 2 Seg Desc 1 | segment descriptor (size, physical address) +** +-----------------------+ +** | ... | more segment descriptors (size, physical address) +** +-----------------------+ +** | Buffer 2 Seg Desc n | +** +-----------------------+ +** | ... | more buffer descriptor blocks ... +** +-----------------------+ +** | Buffer n Context | +** +-----------------------+ +** | Buffer n Seg Count | +** +-----------------------+ +** | Buffer n Seg Desc 1 | +** +-----------------------+ +** | ... | +** +-----------------------+ +** | Buffer n Seg Desc n | +** +-----------------------+ +** +** +** A TCB for one contigous packet buffer would look like the following: +** +** 32 0 +** +-----------------------+ +** | 1 | one buffer in the TCB +** +-----------------------+ +** | | user's buffer reference +** +-----------------------+ +** | 1 | one segment buffer +** +-----------------------+ _ +** | | size \ +** +-----------------------+ \ segment descriptor +** | | physical address of buffer / +** +-----------------------+ _/ +** +*/ + + /* Buffer Segment Descriptor */ +typedef struct +{ + U32 size; + U32 phyAddress; +} + BSD, *PBSD; + +typedef PU32 PRCTCB; +/* +** ------------------------------------------------------------------------- +** Exported functions comprising the API to the message transport layer +** ------------------------------------------------------------------------- +*/ + + + /* + ** InitRCApiMsgLayer() + ** + ** Called once prior to using the API message transport layer. User + ** provides both the physical and virual address of a locked page buffer + ** that is used as a private buffer for the RedCreek API message + ** transport layer. This buffer must be a contigous memory block of a + ** minimum of 16K bytes and long word aligned. The user also must provide + ** the base address of the RedCreek PCI adapter assigned by BIOS or operating + ** system. The user provided value AdapterID is a zero based index of the + ** Ravlin 45/PCI adapter. This interface number is used in all subsequent API + ** calls to identify which adpapter for which the function is intended. + ** Up to sixteen interfaces are supported with this API. + ** + ** Inputs: AdapterID - interface number from 0 to 15 + ** pciBaseAddr - virual base address of PCI (set by BIOS) + ** p_msgbuf - virual address to private message block (min. 16K) + ** p_phymsgbuf - physical address of private message block + ** TransmitCallbackFunction - address of user's TX callback function + ** ReceiveCallbackFunction - address of user's RX callback function + ** + */ +RC_RETURN InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr, + PU8 p_msgbuf, PU8 p_phymsgbuf, + PFNTXCALLBACK TransmitCallbackFunction, + PFNRXCALLBACK ReceiveCallbackFunction, + PFNCALLBACK RebootCallbackFunction); + + /* + ** RCSetRavlinIPandMask() + ** + ** Set the Ravlin 45/PCI cards IP address and network mask. + ** + ** IP address and mask must be in network byte order. + ** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be + ** 0x04030201 and 0x00FFFFFF on a little endian machine. + ** + */ +RC_RETURN RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask); + + +/* +** ========================================================================= +** RCGetRavlinIPandMask() +** +** get the IP address and MASK from the card +** +** ========================================================================= +*/ +RC_RETURN +RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, + PFNWAITCALLBACK WaitCallback); + + /* + ** RCProcMsgQ() + ** + ** Called from user's polling loop or Interrupt Service Routine for a PCI + ** interrupt from the RedCreek PCI adapter. User responsible for determining + ** and hooking the PCI interrupt. This function will call the registered + ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction, + ** if a TX or RX transaction has completed. + */ +void RCProcMsgQ(U16 AdapterID); + + + /* + ** Disable and Enable Adapter interrupts. Adapter interrupts are enabled at + ** Init time but can be disabled and re-enabled through these two function calls. + ** Packets will still be put into any posted recieved buffers and packets will + ** be sent through RCSendPacket() functions. Disabling Adapter interrupts + ** will prevent hardware interrupt to host even though the outbound msg + ** queue is not emtpy. + */ +RC_RETURN RCEnableAdapterInterrupts(U16 adapterID); +RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID); + + + /* + ** RCPostRecvBuffers() + ** + ** Post user's page locked buffers for use by the PCI adapter to + ** return ethernet packets received from the LAN. Transaction Control Block, + ** provided by user, contains buffer descriptor(s) which includes a buffer + ** context number along with buffer size and physical address. See TCB above. + ** The buffer context and actual packet length are returned to the + ** ReceiveCallbackFunction when packets have been received. Buffers posted + ** to the RedCreek adapter are considered owned by the adapter until the + ** context is return to user through the ReceiveCallbackFunction. + */ +RC_RETURN RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransactionCtrlBlock); +#define MAX_NMBR_POST_BUFFERS_PER_MSG 32 + + /* + ** RCSendPacket() + ** + ** Send user's ethernet packet from a locked page buffer. + ** Packet must have full MAC header, however without a CRC. + ** Initiator context is a user provided value that is returned + ** to the TransmitCallbackFunction when packet buffer is free. + ** Transmit buffer are considered owned by the adapter until context's + ** returned to user through the TransmitCallbackFunction. + */ +RC_RETURN RCSendPacket(U16 AdapterID, + U32 context, + PRCTCB pTransactionCtrlBlock); + + + /* Ethernet Link Statistics structure */ +typedef struct tag_RC_link_stats +{ + U32 TX_good; /* good transmit frames */ + U32 TX_maxcol; /* frames not TX due to MAX collisions */ + U32 TX_latecol; /* frames not TX due to late collisions */ + U32 TX_urun; /* frames not TX due to DMA underrun */ + U32 TX_crs; /* frames TX with lost carrier sense */ + U32 TX_def; /* frames deferred due to activity on link */ + U32 TX_singlecol; /* frames TX with one and only on collision */ + U32 TX_multcol; /* frames TX with more than one collision */ + U32 TX_totcol; /* total collisions detected during TX */ + U32 Rcv_good; /* good frames received */ + U32 Rcv_CRCerr; /* frames RX and discarded with CRC errors */ + U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */ + U32 Rcv_reserr; /* good frames discarded due to no RX buffer */ + U32 Rcv_orun; /* RX frames lost due to FIFO overrun */ + U32 Rcv_cdt; /* RX frames with collision during RX */ + U32 Rcv_runt; /* RX frames shorter than 64 bytes */ +} + RCLINKSTATS, *P_RCLINKSTATS; + + /* + ** RCGetLinkStatistics() + ** + ** Returns link statistics in user's structure at address StatsReturnAddr + ** If given, not NULL, the function WaitCallback is called during the wait + ** loop while waiting for the adapter to respond. + */ +RC_RETURN RCGetLinkStatistics(U16 AdapterID, + P_RCLINKSTATS StatsReturnAddr, + PFNWAITCALLBACK WaitCallback); + + /* + ** RCGetLinkStatus() + ** + ** Return link status, up or down, to user's location addressed by ReturnAddr. + ** If given, not NULL, the function WaitCallback is called during the wait + ** loop while waiting for the adapter to respond. + */ +RC_RETURN RCGetLinkStatus(U16 AdapterID, + PU32 pReturnStatus, + PFNWAITCALLBACK WaitCallback); + + /* Link Status defines - value returned in pReturnStatus */ +#define LAN_LINK_STATUS_DOWN 0 +#define LAN_LINK_STATUS_UP 1 + + /* + ** RCGetMAC() + ** + ** Get the current MAC address assigned to user. RedCreek Ravlin 45/PCI + ** has two MAC addresses. One which is private to the PCI Card, and + ** another MAC which is given to the user as its link layer MAC address. The + ** adapter runs in promiscous mode because of the dual address requirement. + ** The MAC address is returned to the unsigned char array pointer to by mac. + */ +RC_RETURN RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback); + + /* + ** RCSetMAC() + ** + ** Set a new user port MAC address. This address will be returned on + ** subsequent RCGetMAC() calls. + */ +RC_RETURN RCSetMAC(U16 AdapterID, PU8 mac); + + /* + ** RCSetLinkSpeed() + ** + ** set adapter's link speed based on given input code. + */ +RC_RETURN RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode); + /* Set link speed codes */ +#define LNK_SPD_AUTO_NEG_NWAY 0 +#define LNK_SPD_100MB_FULL 1 +#define LNK_SPD_100MB_HALF 2 +#define LNK_SPD_10MB_FULL 3 +#define LNK_SPD_10MB_HALF 4 + + + + + /* + ** RCGetLinkSpeed() + ** + ** Return link speed code. + */ + /* Return link speed codes */ +#define LNK_SPD_UNKNOWN 0 +#define LNK_SPD_100MB_FULL 1 +#define LNK_SPD_100MB_HALF 2 +#define LNK_SPD_10MB_FULL 3 +#define LNK_SPD_10MB_HALF 4 + +RC_RETURN +RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback); + +/* +** ========================================================================= +** RCReportDriverCapability(U16 AdapterID, U32 capability) +** +** Currently defined bits: +** WARM_REBOOT_CAPABLE 0x01 +** +** ========================================================================= +*/ +RC_RETURN +RCReportDriverCapability(U16 AdapterID, U32 capability); + +/* +** RCGetFirmwareVer() +** +** Return firmware version in the form "SoftwareVersion : Bt BootVersion" +** +** WARNING: user's space pointed to by pFirmString should be at least 60 bytes. +*/ +RC_RETURN +RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback); + +/* +** ---------------------------------------------- +** LAN adapter Reset and Shutdown functions +** ---------------------------------------------- +*/ + /* resource flag bit assignments for RCResetLANCard() & RCShutdownLANCard() */ +#define RC_RESOURCE_RETURN_POSTED_RX_BUCKETS 0x0001 +#define RC_RESOURCE_RETURN_PEND_TX_BUFFERS 0x0002 + + /* + ** RCResetLANCard() + ** + ** Reset LAN card operation. Causes a software reset of the ethernet + ** controller and restarts the command and receive units. Depending on + ** the ResourceFlags given, the buffers are either returned to the + ** host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER and + ** detailed status of RC_DSC_CANCELED (new receive buffers must be + ** posted after issuing this) OR the buffers are kept and reused by + ** the ethernet controller. If CallbackFunction is not NULL, the function + ** will be called when the reset is complete. If the CallbackFunction is + ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset + ** to complete (please disable adapter interrupts during this method). + ** Any outstanding transmit or receive buffers that are complete will be + ** returned via the normal reply messages before the requested resource + ** buffers are returned. + ** A call to RCPostRecvBuffers() is needed to return the ethernet to full + ** operation if the receive buffers were returned during LANReset. + ** Note: The IOP status is not affected by a LAN reset. + */ +RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction); + + + /* + ** RCShutdownLANCard() + ** + ** Shutdown LAN card operation and put into an idle (suspended) state. + ** The LAN card is restarted with RCResetLANCard() function. + ** Depending on the ResourceFlags given, the buffers are either returned + ** to the host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER + ** and detailed status of RC_DSC_CANCELED (new receive buffers must be + ** posted after issuing this) OR the buffers are kept and reused by + ** the ethernet controller. If CallbackFunction is not NULL, the function + ** will be called when the reset is complete. If the CallbackFunction is + ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset + ** to complete (please disable adapter interrupts during this method). + ** Any outstanding transmit or receive buffers that are complete will be + ** returned via the normal reply messages before the requested resource + ** buffers are returned. + ** Note: The IOP status is not affected by a LAN shutdown. + */ +RC_RETURN +RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction); + + /* + ** RCResetAdapter(); + ** Initializes ADAPTERState to ADAPTER_STATE_RESET. + ** Stops access to outbound message Q. + ** Discards any outstanding transmit or posted receive buffers. + ** Clears outbound message Q. + */ +RC_RETURN +RCResetAdapter(U16 AdapterID); + +#endif /* RCMTL_H */ diff -u --recursive --new-file v2.0.35/linux/drivers/net/rcpci45.c linux/drivers/net/rcpci45.c --- v2.0.35/linux/drivers/net/rcpci45.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/rcpci45.c Sun Nov 15 10:33:03 1998 @@ -0,0 +1,1332 @@ +/* +** RCpci45.c +** +** +** +** --------------------------------------------------------------------- +** --- Copyright (c) 1998, RedCreek Communications Inc. --- +** --- All rights reserved. --- +** --------------------------------------------------------------------- +** +** Written by Pete Popov and Brian Moyle. +** +** Known Problems +** +** Billions and Billions... +** +** ... apparently added by Brian. Pete knows of no bugs. +** +** TODO: +** -Get rid of the wait loops in the API and replace them +** with system independent delays ...something like +** "delayms(2)". +** +** 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. +** +***************************************************************************/ + +#define __NO_VERSION__ /* don't define kernel_verion in module.h */ + +static char *version = +"RedCreek Communications PCI linux driver version 1.32 Beta\n"; + + +#include +#include +#include + +static char kernel_version [] = UTS_RELEASE; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For NR_IRQS only. */ +#include +#include + +#include +#include +#include +#include + +#define RC_LINUX_MODULE +#include "rcmtl.h" +#include "rcif.h" + +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) + +#define FREE_IRQ(irqnum, dev) free_irq(irqnum) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n) +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) + +#define NEW_MULTICAST +#include + +/* PCI/45 Configuration space values */ +#define RC_PCI45_VENDOR_ID 0x4916 +#define RC_PCI45_DEVICE_ID 0x1960 + +#define MAX_ETHER_SIZE 1520 +#define MAX_NMBR_RCV_BUFFERS 96 +#define RC_POSTED_BUFFERS_LOW_MARK MAX_NMBR_RCV_BUFFERS-16 +#define BD_SIZE 3 /* Bucket Descriptor size */ +#define BD_LEN_OFFSET 2 /* Bucket Descriptor offset to length field */ + + +/* RedCreek LAN device Target ID */ +#define RC_LAN_TARGET_ID 0x10 +/* RedCreek's OSM default LAN receive Initiator */ +#define DEFAULT_RECV_INIT_CONTEXT 0xA17 + + +static U32 DriverControlWord = 0; + +static void rc_timer(unsigned long); + +/* + * Driver Private Area, DPA. + */ +typedef struct +{ + + /* + * pointer to the device structure which is part + * of the interface to the Linux kernel. + */ + struct device *dev; + + char devname[8]; /* "ethN" string */ + U8 id; /* the AdapterID */ + U32 pci_addr; /* the pci address of the adapter */ + U32 bus; + U32 function; + struct timer_list timer; /* timer */ + struct enet_statistics stats; /* the statistics structure */ + struct device *next; /* points to the next RC adapter */ + unsigned long numOutRcvBuffers;/* number of outstanding receive buffers*/ + unsigned char shutdown; + unsigned char reboot; + unsigned char nexus; + PU8 PLanApiPA; /* Pointer to Lan Api Private Area */ + +} +DPA, *PDPA; + +#define MAX_ADAPTERS 32 + +static PDPA PCIAdapters[MAX_ADAPTERS] = +{ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + + +static int RCscan(void); +static struct device +*RCfound_device(struct device *, int, int, int, int, int, int); + +static int RCprobe1(struct device *); +static int RCopen(struct device *); +static int RC_xmit_packet(struct sk_buff *, struct device *); +static void RCinterrupt(int, void *, struct pt_regs *); +static int RCclose(struct device *dev); +static struct enet_statistics *RCget_stats(struct device *); +static int RCioctl(struct device *, struct ifreq *, int); +static int RCconfig(struct device *, struct ifmap *); +static void RCxmit_callback(U32, U16, PU32, U16); +static void RCrecv_callback(U32, U8, U32, PU32, U16); +static void RCreset_callback(U32, U32, U32, U16); +static void RCreboot_callback(U32, U32, U32, U16); +static int RC_allocate_and_post_buffers(struct device *, int); + + +/* A list of all installed RC devices, for removing the driver module. */ +static struct device *root_RCdev = NULL; + +#ifdef MODULE +int init_module(void) +#else +int rcpci_probe(struct netdevice *dev) +#endif +{ + int cards_found; + + printk(version); + + root_RCdev = NULL; + cards_found = RCscan(); +#ifdef MODULE + return cards_found ? 0 : -ENODEV; +#else + return -1; +#endif +} + +static int RCscan(void) +{ + int cards_found = 0; + struct device *dev = 0; + + if (pcibios_present()) + { + static int pci_index = 0; + unsigned char pci_bus, pci_device_fn; + int scan_status; + int board_index = 0; + + for (;pci_index < 0xff; pci_index++) + { + unsigned char pci_irq_line; + unsigned short pci_command, vendor, device, class; + unsigned int pci_ioaddr; + + + scan_status = + (pcibios_find_device (RC_PCI45_VENDOR_ID, + RC_PCI45_DEVICE_ID, + pci_index, + &pci_bus, + &pci_device_fn)); +#ifdef RCDEBUG + printk("rc scan_status = 0x%X\n", scan_status); +#endif + if (scan_status != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(pci_bus, + pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, + pci_device_fn, + PCI_DEVICE_ID, &device); + pcibios_read_config_byte(pci_bus, + pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, + pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + pcibios_read_config_word(pci_bus, + pci_device_fn, + PCI_CLASS_DEVICE, &class); + + pci_ioaddr &= ~0xf; + +#ifdef RCDEBUG + printk("rc: Found RedCreek PCI adapter\n"); + printk("rc: pci class = 0x%x 0x%x \n", class, class>>8); + printk("rc: pci_bus = %d, pci_device_fn = %d\n", pci_bus, pci_device_fn); + printk("rc: pci_irq_line = 0x%x \n", pci_irq_line); + printk("rc: pci_ioaddr = 0x%x\n", pci_ioaddr); +#endif + +#if 0 + if (check_region(pci_ioaddr, 32768)) + { + printk("rc: check_region failed\n"); + continue; + } + else + { + printk("rc: check_region passed\n"); + } +#endif + + /* + * Get and check the bus-master and latency values. + * Some PCI BIOSes fail to set the master-enable bit. + */ + + pcibios_read_config_word(pci_bus, + pci_device_fn, + PCI_COMMAND, + &pci_command); + if ( ! (pci_command & PCI_COMMAND_MASTER)) { + printk("rc: PCI Master Bit has not been set!\n"); + + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus, + pci_device_fn, + PCI_COMMAND, + pci_command); + } + if ( ! (pci_command & PCI_COMMAND_MEMORY)) { + /* + * If the BIOS did not set the memory enable bit, what else + * did it not initialize? Skip this adapter. + */ + printk("rc: Adapter %d, PCI Memory Bit has not been set!\n", + cards_found); + printk("rc: Bios problem? \n"); + continue; + } + + dev = RCfound_device(dev, pci_ioaddr, pci_irq_line, + pci_bus, pci_device_fn, + board_index++, cards_found); + + if (dev) { + dev = 0; + cards_found++; + } + } + } + printk("rc: found %d cards \n", cards_found); + return cards_found; +} + +static struct device * +RCfound_device(struct device *dev, int memaddr, int irq, + int bus, int function, int product_index, int card_idx) +{ + int dev_size = 32768; + unsigned long *vaddr=0; + PDPA pDpa; + int init_status; + + /* + * Allocate and fill new device structure. + * We need enough for struct device plus DPA plus the LAN API private + * area, which requires a minimum of 16KB. The top of the allocated + * area will be assigned to struct device; the next chunk will be + * assigned to DPA; and finally, the rest will be assigned to the + * the LAN API layer. + */ + dev = (struct device *) kmalloc(dev_size, GFP_DMA | GFP_KERNEL |GFP_ATOMIC); + memset(dev, 0, dev_size); +#ifdef RCDEBUG + printk("rc: dev = 0x%08X\n", (uint)dev); +#endif + + /* + * dev->priv will point to the start of DPA. + */ + dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15); + pDpa = dev->priv; + dev->name = pDpa->devname; + + pDpa->dev = dev; /* this is just for easy reference */ + pDpa->function = function; + pDpa->bus = bus; + pDpa->id = card_idx; /* the device number */ + pDpa->pci_addr = memaddr; + PCIAdapters[card_idx] = pDpa; +#ifdef RCDEBUG + printk("rc: pDpa = 0x%x, id = %d \n", (uint)pDpa, (uint)pDpa->id); +#endif + + /* + * Save the starting address of the LAN API private area. We'll + * pass that to InitRCApiMsgLayer(). + */ + pDpa->PLanApiPA = (void *)(((long)pDpa + sizeof(DPA) + 0xff) & ~0xff); +#ifdef RCDEBUG + printk("rc: pDpa->PLanApiPA = 0x%x\n", (uint)pDpa->PLanApiPA); +#endif + + /* The adapter is accessable through memory-access read/write, not + * I/O read/write. Thus, we need to map it to some virtual address + * area in order to access the registers are normal memory. + */ + vaddr = (ulong *) vremap (memaddr, 32768); +#ifdef RCDEBUG + printk("rc: RCfound_device: 0x%x, priv = 0x%x, vaddr = 0x%x\n", + (uint)dev, (uint)dev->priv, (uint)vaddr); +#endif + dev->base_addr = (unsigned long)vaddr; + dev->irq = irq; + dev->interrupt = 0; + + /* + * Request a shared interrupt line. + */ + if ( request_irq(dev->irq, (void *)RCinterrupt, + SA_INTERRUPT|SA_SHIRQ, "RedCreek VPN Adapter", dev) ) + { + printk( "RC PCI 45: %s: unable to get IRQ %d\n", (PU8)dev->name, (uint)dev->irq ); + vfree(vaddr); + kfree(dev); + return 0; + } + + init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr, + pDpa->PLanApiPA, pDpa->PLanApiPA, + (PFNTXCALLBACK)RCxmit_callback, + (PFNRXCALLBACK)RCrecv_callback, + (PFNCALLBACK)RCreboot_callback); +#ifdef RCDEBUG + printk("rc: msg initted: status = 0x%x\n", init_status); +#endif + if (init_status) + { + printk("rc: Unable to initialize msg layer\n"); + free_irq(dev->irq, dev); + vfree(vaddr); + kfree(dev); + return 0; + } + if (RCGetMAC(pDpa->id, dev->dev_addr, NULL)) + { + printk("rc: Unable to get adapter MAC\n"); + free_irq(dev->irq, dev); + vfree(vaddr); + kfree(dev); + return 0; + } + + DriverControlWord |= WARM_REBOOT_CAPABLE; + RCReportDriverCapability(pDpa->id, DriverControlWord); + + dev->init = RCprobe1; + ether_setup(dev); /* linux kernel interface */ + + pDpa->next = root_RCdev; + root_RCdev = dev; + + if (register_netdev(dev) != 0) /* linux kernel interface */ + { + printk("rc: unable to register device \n"); + free_irq(dev->irq, dev); + vfree(vaddr); + kfree(dev); + return 0; + } + return dev; +} + +static int RCprobe1(struct device *dev) +{ + dev->open = RCopen; + dev->hard_start_xmit = RC_xmit_packet; + dev->stop = RCclose; + dev->get_stats = RCget_stats; + dev->do_ioctl = RCioctl; + dev->set_config = RCconfig; + return 0; +} + +static int +RCopen(struct device *dev) +{ + int post_buffers = MAX_NMBR_RCV_BUFFERS; + PDPA pDpa = (PDPA) dev->priv; + int count = 0; + int requested = 0; + +#ifdef RCDEBUG + printk("rc: RCopen\n"); +#endif + RCEnableAdapterInterrupts(pDpa->id); + + if (pDpa->nexus) + { + /* This is not the first time RCopen is called. Thus, + * the interface was previously opened and later closed + * by RCclose(). RCclose() does a Shutdown; to wake up + * the adapter, a reset is mandatory before we can post + * receive buffers. However, if the adapter initiated + * a reboot while the interface was closed -- and interrupts + * were turned off -- we need will need to reinitialize + * the adapter, rather than simply waking it up. + */ + printk("rc: Waking up adapter...\n"); + RCResetLANCard(pDpa->id,0,0,0); + } + else + { + pDpa->nexus = 1; + } + + while(post_buffers) + { + if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG) + requested = MAX_NMBR_POST_BUFFERS_PER_MSG; + else + requested = post_buffers; + count = RC_allocate_and_post_buffers(dev, requested); + + if ( count < requested ) + { + /* + * Check to see if we were able to post any buffers at all. + */ + if (post_buffers == MAX_NMBR_RCV_BUFFERS) + { + printk("rc: Error RCopen: not able to allocate any buffers\r\n"); + return(-ENOMEM); + } + printk("rc: Warning RCopen: not able to allocate all requested buffers\r\n"); + break; /* we'll try to post more buffers later */ + } + else + post_buffers -= count; + } + pDpa->numOutRcvBuffers = MAX_NMBR_RCV_BUFFERS - post_buffers; + pDpa->shutdown = 0; /* just in case */ +#ifdef RCDEBUG + printk("rc: RCopen: posted %d buffers\n", (uint)pDpa->numOutRcvBuffers); +#endif + MOD_INC_USE_COUNT; + return 0; +} + +static int +RC_xmit_packet(struct sk_buff *skb, struct device *dev) +{ + + PDPA pDpa = (PDPA) dev->priv; + singleTCB tcb; + psingleTCB ptcb = &tcb; + RC_RETURN status = 0; + + if (dev->tbusy || pDpa->shutdown || pDpa->reboot) + { +#ifdef RCDEBUG + printk("rc: RC_xmit_packet: tbusy!\n"); +#endif + return 1; + } + + if ( skb->len <= 0 ) + { + printk("RC_xmit_packet: skb->len less than 0!\n"); + return 0; + } + + /* + * The user is free to reuse the TCB after RCSendPacket() returns, since + * the function copies the necessary info into its own private space. Thus, + * our TCB can be a local structure. The skb, on the other hand, will be + * freed up in our interrupt handler. + */ + ptcb->bcount = 1; + /* + * we'll get the context when the adapter interrupts us to tell us that + * the transmision is done. At that time, we can free skb. + */ + ptcb->b.context = (U32)skb; + ptcb->b.scount = 1; + ptcb->b.size = skb->len; + ptcb->b.addr = (U32)skb->data; + +#ifdef RCDEBUG + printk("rc: RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n", + (uint)skb, (uint)pDpa, (uint)pDpa->id, (uint)ptcb); +#endif + if ( (status = RCSendPacket(pDpa->id, (U32)NULL, (PRCTCB)ptcb)) + != RC_RTN_NO_ERROR) + { +#ifdef RCDEBUG + printk("rc: RC send error 0x%x\n", (uint)status); +#endif + dev->tbusy = 1; + } + else + { + dev->trans_start = jiffies; + // dev->tbusy = 0; + } + /* + * That's it! + */ + return 0; +} + +/* + * RCxmit_callback() + * + * The transmit callback routine. It's called by RCProcMsgQ() + * because the adapter is done with one or more transmit buffers and + * it's returning them to us, or we asked the adapter to return the + * outstanding transmit buffers by calling RCResetLANCard() with + * RC_RESOURCE_RETURN_PEND_TX_BUFFERS flag. + * All we need to do is free the buffers. + */ +static void +RCxmit_callback(U32 Status, + U16 PcktCount, + PU32 BufferContext, + U16 AdapterID) +{ + struct sk_buff *skb; + PDPA pDpa; + struct device *dev; + + pDpa = PCIAdapters[AdapterID]; + if (!pDpa) + { + printk("rc: Fatal error: xmit callback, !pDpa\n"); + return; + } + dev = pDpa->dev; + + // printk("xmit_callback: Status = 0x%x\n", (uint)Status); + if (Status != RC_REPLY_STATUS_SUCCESS) + { + printk("rc: xmit_callback: Status = 0x%x\n", (uint)Status); + } +#ifdef RCDEBUG + if (pDpa->shutdown || pDpa->reboot) + printk("rc: xmit callback: shutdown||reboot\n"); +#endif + +#ifdef RCDEBUG + printk("rc: xmit_callback: PcktCount = %d, BC = 0x%x\n", + (uint)PcktCount, (uint)BufferContext); +#endif + while (PcktCount--) + { + skb = (struct sk_buff *)(BufferContext[0]); +#ifdef RCDEBUG + printk("rc: skb = 0x%x\n", (uint)skb); +#endif + BufferContext++; + dev_kfree_skb (skb, FREE_WRITE); + } + dev->tbusy = 0; + +} + +static void +RCreset_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID) +{ + PDPA pDpa; + struct device *dev; + + pDpa = PCIAdapters[AdapterID]; + dev = pDpa->dev; +#ifdef RCDEBUG + printk("rc: RCreset_callback Status 0x%x\n", (uint)Status); +#endif + /* + * Check to see why we were called. + */ + if (pDpa->shutdown) + { + printk("rc: Shutting down interface\n"); + pDpa->shutdown = 0; + pDpa->reboot = 0; + MOD_DEC_USE_COUNT; + } + else if (pDpa->reboot) + { + printk("rc: reboot, shutdown adapter\n"); + /* + * We don't set any of the flags in RCShutdownLANCard() + * and we don't pass a callback routine to it. + * The adapter will have already initiated the reboot by + * the time the function returns. + */ + RCDisableAdapterInterrupts(pDpa->id); + RCShutdownLANCard(pDpa->id,0,0,0); + printk("rc: scheduling timer...\n"); + init_timer(&pDpa->timer); + pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */ + pDpa->timer.data = (unsigned long)dev; + pDpa->timer.function = &rc_timer; /* timer handler */ + add_timer(&pDpa->timer); + } + + + +} + +static void +RCreboot_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID) +{ + PDPA pDpa; + + pDpa = PCIAdapters[AdapterID]; +#ifdef RCDEBUG + printk("rc: RCreboot: rcv buffers outstanding = %d\n", + (uint)pDpa->numOutRcvBuffers); +#endif + if (pDpa->shutdown) + { + printk("rc: skipping reboot sequence -- shutdown already initiated\n"); + return; + } + pDpa->reboot = 1; + /* + * OK, we reset the adapter and ask it to return all + * outstanding transmit buffers as well as the posted + * receive buffers. When the adapter is done returning + * those buffers, it will call our RCreset_callback() + * routine. In that routine, we'll call RCShutdownLANCard() + * to tell the adapter that it's OK to start the reboot and + * schedule a timer callback routine to execute 3 seconds + * later; this routine will reinitialize the adapter at that time. + */ + RCResetLANCard(pDpa->id, + RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | + RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0, + (PFNCALLBACK)RCreset_callback); +} + + +int broadcast_packet(unsigned char * address) +{ + int i; + for (i=0; i<6; i++) + if (address[i] != 0xff) return 0; + + return 1; +} + +/* + * RCrecv_callback() + * + * The receive packet callback routine. This is called by + * RCProcMsgQ() after the adapter posts buffers which have been + * filled (one ethernet packet per buffer). + */ +static void +RCrecv_callback(U32 Status, + U8 PktCount, + U32 BucketsRemain, + PU32 PacketDescBlock, + U16 AdapterID) +{ + + U32 len, count; + PDPA pDpa; + struct sk_buff *skb; + struct device *dev; + singleTCB tcb; + psingleTCB ptcb = &tcb; + + + pDpa = PCIAdapters[AdapterID]; + dev = pDpa->dev; + + ptcb->bcount = 1; + +#ifdef RCDEBUG + printk("rc: RCrecv_callback: 0x%x, 0x%x, 0x%x\n", + (uint)PktCount, (uint)BucketsRemain, (uint)PacketDescBlock); +#endif + +#ifdef RCDEBUG + if ((pDpa->shutdown || pDpa->reboot) && !Status) + printk("shutdown||reboot && !Status: PktCount = %d\n",PktCount); +#endif + + if ( (Status != RC_REPLY_STATUS_SUCCESS) || pDpa->shutdown) + { + /* + * Free whatever buffers the adapter returned, but don't + * pass them to the kernel. + */ + + if (!pDpa->shutdown && !pDpa->reboot) + printk("rc: RCrecv error: status = 0x%x\n", (uint)Status); + else + printk("rc: Returning %d buffers, status = 0x%x\n", + PktCount, (uint)Status); + /* + * TO DO: check the nature of the failure and put the adapter in + * failed mode if it's a hard failure. Send a reset to the adapter + * and free all outstanding memory. + */ + if (Status == RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER) + { +#ifdef RCDEBUG + printk("RCrecv status ABORT NO DATA TRANSFER\n"); +#endif + } + /* check for reset status: RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */ + if (PacketDescBlock) + { + while(PktCount--) + { + skb = (struct sk_buff *)PacketDescBlock[0]; + skb->free = 1; + skb->lock = 0; +#ifdef RCDEBUG + printk("free skb 0x%p\n", skb); +#endif + dev_kfree_skb(skb, FREE_READ); + pDpa->numOutRcvBuffers--; + PacketDescBlock += BD_SIZE; /* point to next context field */ + } + } + return; + } + else + { + while(PktCount--) + { + skb = (struct sk_buff *)PacketDescBlock[0]; +#ifdef RCDEBUG + if (pDpa->shutdown) + printk("shutdown: skb=0x%x\n", (uint)skb); + + printk("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", (uint)skb, + (uint)skb->data[0], (uint)skb->data[1], (uint)skb->data[2], + (uint)skb->data[3], (uint)skb->data[4], (uint)skb->data[5]); +#endif + if ( (memcmp(dev->dev_addr, skb->data, 6)) && + (!broadcast_packet(skb->data))) + { + /* + * Re-post the buffer to the adapter. Since the adapter usually + * return 1 to 2 receive buffers at a time, it's not too inefficient + * post one buffer at a time but ... may be that should be + * optimized at some point. + */ + ptcb->b.context = (U32)skb; + ptcb->b.scount = 1; + ptcb->b.size = MAX_ETHER_SIZE; + ptcb->b.addr = (U32)skb->data; + + if ( RCPostRecvBuffers(pDpa->id, (PRCTCB)ptcb ) != RC_RTN_NO_ERROR) + { + printk("rc: RCrecv_callback: post buffer failed!\n"); + skb->free = 1; + dev_kfree_skb(skb, FREE_READ); + } + else + { + pDpa->numOutRcvBuffers++; + } + } + else + { + len = PacketDescBlock[2]; + skb->dev = dev; + skb_put( skb, len ); /* adjust length and tail */ + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); /* send the packet to the kernel */ + dev->last_rx = jiffies; + } + pDpa->numOutRcvBuffers--; + PacketDescBlock += BD_SIZE; /* point to next context field */ + } + } + + /* + * Replenish the posted receive buffers. + * DO NOT replenish buffers if the driver has already + * initiated a reboot or shutdown! + */ + + if (!pDpa->shutdown && !pDpa->reboot) + { + count = RC_allocate_and_post_buffers(dev, + MAX_NMBR_RCV_BUFFERS-pDpa->numOutRcvBuffers); + pDpa->numOutRcvBuffers += count; + } + +} + +/* + * RCinterrupt() + * + * Interrupt handler. + * This routine sets up a couple of pointers and calls + * RCProcMsgQ(), which in turn process the message and + * calls one of our callback functions. + */ +static void +RCinterrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + + PDPA pDpa; + struct device *dev = (struct device *)(dev_id); + + pDpa = (PDPA) (dev->priv); + + if (pDpa->shutdown) + printk("rc: shutdown: service irq\n"); + +#ifdef RCDEBUG + printk("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n", + (uint)pDpa, (uint)dev, (uint)pDpa->id); + printk("dev = 0x%x\n", (uint)dev); +#endif + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + dev->interrupt = 1; + + RCProcMsgQ(pDpa->id); + dev->interrupt = 0; + + return; +} + +#define REBOOT_REINIT_RETRY_LIMIT 10 +static void rc_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + PDPA pDpa = (PDPA) (dev->priv); + int init_status; + static int retry = 0; + int post_buffers = MAX_NMBR_RCV_BUFFERS; + int count = 0; + int requested = 0; + + if (pDpa->reboot) + { + + init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr, + pDpa->PLanApiPA, pDpa->PLanApiPA, + (PFNTXCALLBACK)RCxmit_callback, + (PFNRXCALLBACK)RCrecv_callback, + (PFNCALLBACK)RCreboot_callback); + + switch(init_status) + { + case RC_RTN_NO_ERROR: + + pDpa->reboot = 0; + pDpa->shutdown = 0; /* just in case */ + RCReportDriverCapability(pDpa->id, DriverControlWord); + RCEnableAdapterInterrupts(pDpa->id); + + if (dev->flags & IFF_UP) + { + while(post_buffers) + { + if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG) + requested = MAX_NMBR_POST_BUFFERS_PER_MSG; + else + requested = post_buffers; + count = RC_allocate_and_post_buffers(dev, requested); + post_buffers -= count; + if ( count < requested ) + break; + } + pDpa->numOutRcvBuffers = + MAX_NMBR_RCV_BUFFERS - post_buffers; + printk("rc: posted %d buffers \r\n", + (uint)pDpa->numOutRcvBuffers); + } + printk("rc: Initialization done.\n"); + return; + case RC_RTN_FREE_Q_EMPTY: + retry++; + printk("rc: inbound free q emtpy\n"); + break; + default: + retry++; + printk("rc: unexpected bad status after reboot\n"); + break; + } + + if (retry > REBOOT_REINIT_RETRY_LIMIT) + { + printk("rc: unable to reinitialize adapter after reboot\n"); + printk("rc: decrementing driver and closing interface\n"); + RCDisableAdapterInterrupts(pDpa->id); + dev->flags &= ~IFF_UP; + MOD_DEC_USE_COUNT; + } + else + { + printk("rc: rescheduling timer...\n"); + init_timer(&pDpa->timer); + pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */ + pDpa->timer.data = (unsigned long)dev; + pDpa->timer.function = &rc_timer; /* timer handler */ + add_timer(&pDpa->timer); + } + } + else + { + printk("rc: timer??\n"); + } +} + +static int +RCclose(struct device *dev) +{ + + PDPA pDpa = (PDPA) dev->priv; + +#ifdef RCDEBUG + printk("rc: RCclose\r\n"); +#endif + if (pDpa->reboot) + { + printk("rc: skipping reset -- adapter already in reboot mode\n"); + dev->flags &= ~IFF_UP; + pDpa->shutdown = 1; + return 0; + } +#ifdef RCDEBUG + printk("rc: receive buffers outstanding: %d\n", + (uint)pDpa->numOutRcvBuffers); +#endif + + pDpa->shutdown = 1; + + /* + * We can't allow the driver to be unloaded until the adapter returns + * all posted receive buffers. It doesn't hurt to tell the adapter + * to return all posted receive buffers and outstanding xmit buffers, + * even if there are none. + */ + + RCShutdownLANCard(pDpa->id, + RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | + RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0, + (PFNCALLBACK)RCreset_callback); + + dev->flags &= ~IFF_UP; + return 0; +} + +static struct enet_statistics * +RCget_stats(struct device *dev) +{ + RCLINKSTATS RCstats; + + PDPA pDpa = dev->priv; + + if (!pDpa) + { + printk("rc: RCget_stats: !pDpa\n"); + return 0; + } + else if (!(dev->flags & IFF_UP)) + { +#ifdef RCDEBUG + printk("rc: RCget_stats: device down\n"); +#endif + return 0; + } + + memset(&RCstats, 0, sizeof(RCLINKSTATS)); + if ( (RCGetLinkStatistics(pDpa->id, &RCstats, (void *)0)) == RC_RTN_NO_ERROR ) + { +#ifdef RCDEBUG + printk("rc: TX_good 0x%x\n", (uint)RCstats.TX_good); + printk("rc: TX_maxcol 0x%x\n", (uint)RCstats.TX_maxcol); + printk("rc: TX_latecol 0x%x\n", (uint)RCstats.TX_latecol); + printk("rc: TX_urun 0x%x\n", (uint)RCstats.TX_urun); + printk("rc: TX_crs 0x%x\n", (uint)RCstats.TX_crs); + printk("rc: TX_def 0x%x\n", (uint)RCstats.TX_def); + printk("rc: TX_singlecol 0x%x\n", (uint)RCstats.TX_singlecol); + printk("rc: TX_multcol 0x%x\n", (uint)RCstats.TX_multcol); + printk("rc: TX_totcol 0x%x\n", (uint)RCstats.TX_totcol); + + printk("rc: Rcv_good 0x%x\n", (uint)RCstats.Rcv_good); + printk("rc: Rcv_CRCerr 0x%x\n", (uint)RCstats.Rcv_CRCerr); + printk("rc: Rcv_alignerr 0x%x\n", (uint)RCstats.Rcv_alignerr); + printk("rc: Rcv_reserr 0x%x\n", (uint)RCstats.Rcv_reserr); + printk("rc: Rcv_orun 0x%x\n", (uint)RCstats.Rcv_orun); + printk("rc: Rcv_cdt 0x%x\n", (uint)RCstats.Rcv_cdt); + printk("rc: Rcv_runt 0x%x\n", (uint)RCstats.Rcv_runt); +#endif + + pDpa->stats.rx_packets = RCstats.Rcv_good; /* total packets received */ + pDpa->stats.tx_packets = RCstats.TX_good; /* total packets transmitted */ + + pDpa->stats.rx_errors = + RCstats.Rcv_CRCerr + + RCstats.Rcv_alignerr + + RCstats.Rcv_reserr + + RCstats.Rcv_orun + + RCstats.Rcv_cdt + + RCstats.Rcv_runt; /* bad packets received */ + + pDpa->stats.tx_errors = + RCstats.TX_urun + + RCstats.TX_crs + + RCstats.TX_def + + RCstats.TX_totcol; /* packet transmit problems */ + + /* + * This needs improvement. + */ + pDpa->stats.rx_dropped = 0; /* no space in linux buffers */ + pDpa->stats.tx_dropped = 0; /* no space available in linux */ + pDpa->stats.multicast = 0; /* multicast packets received */ + pDpa->stats.collisions = RCstats.TX_totcol; + + /* detailed rx_errors: */ + pDpa->stats.rx_length_errors = 0; + pDpa->stats.rx_over_errors = RCstats.Rcv_orun; /* receiver ring buff overflow */ + pDpa->stats.rx_crc_errors = RCstats.Rcv_CRCerr; /* recved pkt with crc error */ + pDpa->stats.rx_frame_errors = 0; /* recv'd frame alignment error */ + pDpa->stats.rx_fifo_errors = 0; /* recv'r fifo overrun */ + pDpa->stats.rx_missed_errors = 0; /* receiver missed packet */ + + /* detailed tx_errors */ + pDpa->stats.tx_aborted_errors = 0; + pDpa->stats.tx_carrier_errors = 0; + pDpa->stats.tx_fifo_errors = 0; + pDpa->stats.tx_heartbeat_errors = 0; + pDpa->stats.tx_window_errors = 0; + + return ((struct enet_statistics *)&(pDpa->stats)); + } + return 0; +} + +static int RCioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + RCuser_struct RCuser; + PDPA pDpa = dev->priv; + +#if RCDEBUG + printk("RCioctl: cmd = 0x%x\n", cmd); +#endif + + switch (cmd) { + + case RCU_PROTOCOL_REV: + /* + * Assign user protocol revision, to tell user-level + * controller program whether or not it's in sync. + */ + rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV; + break; + + + case RCU_COMMAND: + { + int error; + + error=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(RCuser)); + if (error) { + return error; + } + memcpy_fromfs(&RCuser, rq->ifr_data, sizeof(RCuser)); + +#ifdef RCDEBUG + printk("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd); +#endif + + switch(RCuser.cmd) + { + case RCUC_GETFWVER: + printk("RC GETFWVER\n"); + RCUD_GETFWVER = &RCuser.RCUS_GETFWVER; + RCGetFirmwareVer(pDpa->id, (PU8) &RCUD_GETFWVER->FirmString, NULL); + break; + case RCUC_GETINFO: + printk("RC GETINFO\n"); + RCUD_GETINFO = &RCuser.RCUS_GETINFO; + RCUD_GETINFO -> mem_start = dev->base_addr; + RCUD_GETINFO -> mem_end = dev->base_addr + 32768; + RCUD_GETINFO -> base_addr = pDpa->pci_addr; + RCUD_GETINFO -> irq = dev->irq; + break; + case RCUC_GETIPANDMASK: + printk("RC GETIPANDMASK\n"); + RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK; + RCGetRavlinIPandMask(pDpa->id, (PU32) &RCUD_GETIPANDMASK->IpAddr, + (PU32) &RCUD_GETIPANDMASK->NetMask, NULL); + break; + case RCUC_GETLINKSTATISTICS: + printk("RC GETLINKSTATISTICS\n"); + RCUD_GETLINKSTATISTICS = &RCuser.RCUS_GETLINKSTATISTICS; + RCGetLinkStatistics(pDpa->id, (P_RCLINKSTATS) &RCUD_GETLINKSTATISTICS->StatsReturn, NULL); + break; + case RCUC_GETLINKSTATUS: + printk("RC GETLINKSTATUS\n"); + RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS; + RCGetLinkStatus(pDpa->id, (PU32) &RCUD_GETLINKSTATUS->ReturnStatus, NULL); + break; + case RCUC_GETMAC: + printk("RC GETMAC\n"); + RCUD_GETMAC = &RCuser.RCUS_GETMAC; + RCGetMAC(pDpa->id, (PU8) &RCUD_GETMAC->mac, NULL); + break; + case RCUC_GETSPEED: + printk("RC GETSPEED\n"); + if (!(dev->flags & IFF_UP)) + { + printk("RCioctl, GETSPEED error: interface down\n"); + return -ENODATA; + } + RCUD_GETSPEED = &RCuser.RCUS_GETSPEED; + RCGetLinkSpeed(pDpa->id, (PU32) &RCUD_GETSPEED->LinkSpeedCode, NULL); + printk("RC speed = 0x%ld\n", RCUD_GETSPEED->LinkSpeedCode); + break; + default: + printk("RC command default\n"); + RCUD_DEFAULT = &RCuser.RCUS_DEFAULT; + RCUD_DEFAULT -> rc = 0x11223344; + break; + } + memcpy_tofs(rq->ifr_data, &RCuser, sizeof(RCuser)); + break; + } /* RCU_COMMAND */ + + default: + printk("RC default\n"); + rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678; + break; + } + return 0; +} + +static int RCconfig(struct device *dev, struct ifmap *map) +{ + /* + * To be completed ... + */ + printk("rc: RCconfig\n"); + return 0; + if (dev->flags & IFF_UP) /* can't act on a running interface */ + return -EBUSY; + + /* Don't allow changing the I/O address */ + if (map->base_addr != dev->base_addr) { + printk(KERN_WARNING "RC pci45: Change I/O address not implemented\n"); + return -EOPNOTSUPP; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + PDPA pDpa; + struct device *next; + + +#ifdef RCDEBUG + printk("rc: RC cleanup_module\n"); + printk("rc: root_RCdev = 0x%x\n", (uint)root_RCdev); +#endif + + + while (root_RCdev) + { + pDpa = (PDPA) root_RCdev->priv; +#ifdef RCDEBUG + printk("rc: cleanup 0x%08X\n", (uint)root_RCdev); +#endif + printk("Adapter reset: 0x%x\n", RCResetAdapter(pDpa->id)); + unregister_netdev(root_RCdev); + next = pDpa->next; + + vfree((unsigned long *)root_RCdev->base_addr); + free_irq( root_RCdev->irq, root_RCdev ); + kfree(root_RCdev); + root_RCdev = next; + } +} +#endif + + +static int +RC_allocate_and_post_buffers(struct device *dev, int numBuffers) +{ + + int i; + PDPA pDpa = (PDPA)dev->priv; + PU32 p; + psingleB pB; + struct sk_buff *skb; + RC_RETURN status; + + if (!numBuffers) + return 0; + else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG) + { +#ifdef RCDEBUG + printk("rc: Too many buffers requested!\n"); + printk("rc: attempting to allocate only 32 buffers\n"); +#endif + numBuffers = 32; + } + + p = (PU32) kmalloc(sizeof(U32) + numBuffers*sizeof(singleB), GFP_ATOMIC); + +#ifdef RCDEBUG + printk("rc: TCB = 0x%x\n", (uint)p); +#endif + + if (!p) + { + printk("rc: RCopen: unable to allocate TCB\n"); + return 0; + } + + p[0] = 0; /* Buffer Count */ + pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */ + +#ifdef RCDEBUG + printk("rc: p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint)p[0], (uint)p, (uint)pB); + printk("rc: pB = 0x%x\n", (uint)pB); +#endif + + for (i=0; icontext = (U32)skb; + pB->scount = 1; /* segment count */ + pB->size = MAX_ETHER_SIZE; + pB->addr = (U32)skb->data; + p[0]++; + pB++; + } + + if ( (status = RCPostRecvBuffers(pDpa->id, (PRCTCB)p )) != RC_RTN_NO_ERROR) + { + printk("rc: Post buffer failed with error code 0x%x!\n", status); + pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */ + while(p[0]) + { + skb = (struct sk_buff *)pB->context; + skb->free = 1; +#ifdef RCDEBUG + printk("rc: freeing 0x%x\n", (uint)skb); +#endif + dev_kfree_skb(skb, FREE_READ); + p[0]--; + pB++; + } +#ifdef RCDEBUG + printk("rc: freed all buffers, p[0] = %ld\n", p[0]); +#endif + } + kfree(p); + return(p[0]); /* return the number of posted buffers */ +} diff -u --recursive --new-file v2.0.35/linux/drivers/net/rtl8139.c linux/drivers/net/rtl8139.c --- v2.0.35/linux/drivers/net/rtl8139.c Mon Jul 13 13:46:30 1998 +++ linux/drivers/net/rtl8139.c Sun Nov 15 10:33:03 1998 @@ -45,7 +45,6 @@ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT ((4000*HZ)/1000) -#include #ifdef MODULE #ifdef MODVERSIONS #include diff -u --recursive --new-file v2.0.35/linux/drivers/net/shaper.c linux/drivers/net/shaper.c --- v2.0.35/linux/drivers/net/shaper.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/shaper.c Sun Nov 15 10:33:03 1998 @@ -0,0 +1,666 @@ +/* + * Simple traffic shaper for Linux NET3. + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.cymru.net + * + * 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. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * + * Algorithm: + * + * Queue Frame: + * Compute time length of frame at regulated speed + * Add frame to queue at appropriate point + * Adjust time length computation for followup frames + * Any frame that falls outside of its boundaries is freed + * + * We work to the following constants + * + * SHAPER_QLEN Maximum queued frames + * SHAPER_LATENCY Bounding latency on a frame. Leaving this latency + * window drops the frame. This stops us queueing + * frames for a long time and confusing a remote + * host. + * SHAPER_MAXSLIP Maximum time a priority frame may jump forward. + * That bounds the penalty we will inflict on low + * priority traffic. + * SHAPER_BURST Time range we call "now" in order to reduce + * system load. The more we make this the burstier + * the behaviour, the better local performance you + * get through packet clustering on routers and the + * worse the remote end gets to judge rtts. + * + * This is designed to handle lower speed links ( < 200K/second or so). We + * run off a 100-150Hz base clock typically. This gives us a resolution at + * 200Kbit/second of about 2Kbit or 256 bytes. Above that our timer + * resolution may start to cause much more burstiness in the traffic. We + * could avoid a lot of that by calling kick_shaper() at the end of the + * tied device transmissions. If you run above about 100K second you + * may need to tune the supposed speed rate for the right values. + * + * BUGS: + * Downing the interface under the shaper before the shaper + * will render your machine defunct. Don't for now shape over + * PPP or SLIP therefore! + * This will be fixed in BETA4 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int sh_debug; /* Debug flag */ + +#define SHAPER_BANNER "Traffic Shaper 0.05 for Linux 2.0 \n" + +/* + * Locking + */ + +static int shaper_lock(struct shaper *sh) +{ + unsigned long flags; + save_flags(flags); + cli(); + /* + * Lock in an interrupt may fail + */ + if(sh->locked && intr_count) + { + restore_flags(flags); + return 0; + } + while(sh->locked) + sleep_on(&sh->wait_queue); + sh->locked=1; + restore_flags(flags); + return 1; +} + +static void shaper_kick(struct shaper *sh); + +static void shaper_unlock(struct shaper *sh) +{ + sh->locked=0; + wake_up(&sh->wait_queue); + shaper_kick(sh); +} + +/* + * Compute clocks on a buffer + */ + +static int shaper_clocks(struct shaper *shaper, struct sk_buff *skb) +{ + int t=skb->len/shaper->bytespertick; + return t; +} + +/* + * Set the speed of a shaper. We compute this in bytes per tick since + * thats how the machine wants to run. Quoted input is in bits per second + * as is traditional (note not BAUD). We assume 8 bit bytes. + */ + +static void shaper_setspeed(struct shaper *shaper, int bitspersec) +{ + shaper->bitspersec=bitspersec; + shaper->bytespertick=(bitspersec/HZ)/8; + if(!shaper->bytespertick) + shaper->bytespertick++; +} + +/* + * Throw a frame at a shaper. + */ + +static int shaper_qframe(struct shaper *shaper, struct sk_buff *skb) +{ + struct sk_buff *ptr; + + /* + * Get ready to work on this shaper. Lock may fail if its + * an interrupt and locked. + */ + + if(!shaper_lock(shaper)) + return -1; + ptr=shaper->sendq.prev; + + /* + * Set up our packet details + */ + + skb->shapelatency=0; + skb->shapeclock=shaper->recovery; + if(skb->shapeclockshapeclock=jiffies; + skb->shapestamp=jiffies; + + /* + * Time slots for this packet. + */ + + skb->shapelen= shaper_clocks(shaper,skb); + +#ifdef SHAPER_COMPLEX /* and broken.. */ + + while(ptr && ptr!=(struct sk_buff *)&shaper->sendq) + { + if(ptr->pripri + && jiffies - ptr->shapeclock < SHAPER_MAXSLIP) + { + struct sk_buff *tmp=ptr->prev; + + /* + * It goes before us therefore we slip the length + * of the new frame. + */ + + ptr->shapeclock+=skb->shapelen; + ptr->shapelatency+=skb->shapelen; + + /* + * The packet may have slipped so far back it + * fell off. + */ + if(ptr->shapelatency > SHAPER_LATENCY) + { + skb_unlink(ptr); + dev_kfree_skb(ptr, FREE_WRITE); + } + ptr=tmp; + } + else + break; + } + if(ptr==NULL || ptr==(struct sk_buff *)&shaper->sendq) + skb_queue_head(&shaper->sendq,skb); + else + { + struct sk_buff *tmp; + /* + * Set the packet clock out time according to the + * frames ahead. Im sure a bit of thought could drop + * this loop. + */ + for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && tmp!=ptr; tmp=tmp->next) + skb->shapeclock+=tmp->shapelen; + skb_append(ptr,skb); + } +#else + { + struct sk_buff *tmp; + /* + * Up our shape clock by the time pending on the queue + * (Should keep this in the shaper as a variable..) + */ + for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && + tmp!=(struct sk_buff *)&shaper->sendq; tmp=tmp->next) + skb->shapeclock+=tmp->shapelen; + /* + * Queue over time. Spill packet. + */ + if(skb->shapeclock-jiffies > SHAPER_LATENCY) + dev_kfree_skb(skb, FREE_WRITE); + else + skb_queue_tail(&shaper->sendq, skb); + } +#endif + if(sh_debug) + printk("Frame queued.\n"); + if(skb_queue_len(&shaper->sendq)>SHAPER_QLEN) + { + ptr=skb_dequeue(&shaper->sendq); + dev_kfree_skb(ptr, FREE_WRITE); + } + shaper_unlock(shaper); + shaper_kick(shaper); + return 0; +} + +/* + * Transmit from a shaper + */ + +static void shaper_queue_xmit(struct shaper *shaper, struct sk_buff *skb) +{ + struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); + if(sh_debug) + printk("Kick frame on %p\n",newskb); + if(newskb) + { + newskb->dev=shaper->dev; + newskb->arp=1; + if(sh_debug) + printk("Kick new frame to %s\n", + shaper->dev->name); + dev_queue_xmit(newskb,shaper->dev,2); + if(sh_debug) + printk("Kicked new frame out.\n"); + dev_kfree_skb(skb, FREE_WRITE); + } +} + +/* + * Timer handler for shaping clock + */ + +static void shaper_timer(unsigned long data) +{ + struct shaper *sh=(struct shaper *)data; + shaper_kick(sh); +} + +/* + * Kick a shaper queue and try and do something sensible with the + * queue. + */ + +static void shaper_kick(struct shaper *shaper) +{ + struct sk_buff *skb; + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&shaper->timer); + + /* + * Shaper unlock will kick + */ + + if(shaper->locked) + { + if(sh_debug) + printk("Shaper locked.\n"); + shaper->timer.expires=jiffies+1; + add_timer(&shaper->timer); + restore_flags(flags); + return; + } + + + /* + * Walk the list (may be empty) + */ + + while((skb=skb_peek(&shaper->sendq))!=NULL) + { + /* + * Each packet due to go out by now (within an error + * of SHAPER_BURST) gets kicked onto the link + */ + + if(sh_debug) + printk("Clock = %d, jiffies = %ld\n", skb->shapeclock, jiffies); + if(skb->shapeclock <= jiffies + SHAPER_BURST) + { + /* + * Pull the frame and get interrupts back on. + */ + + skb_unlink(skb); + if (shaper->recovery < skb->shapeclock + skb->shapelen) + shaper->recovery = skb->shapeclock + skb->shapelen; + restore_flags(flags); + + /* + * Pass on to the physical target device via + * our low level packet thrower. + */ + + skb->shapepend=0; + shaper_queue_xmit(shaper, skb); /* Fire */ + cli(); + } + else + break; + } + + /* + * Next kick. + */ + + if(skb!=NULL) + { + del_timer(&shaper->timer); + shaper->timer.expires=skb->shapeclock; + add_timer(&shaper->timer); + } + + /* + * Interrupts on, mission complete + */ + + restore_flags(flags); +} + + +/* + * Flush the shaper queues on a closedown + */ + +static void shaper_flush(struct shaper *shaper) +{ + struct sk_buff *skb; + while((skb=skb_dequeue(&shaper->sendq))!=NULL) + dev_kfree_skb(skb, FREE_WRITE); +} + +/* + * Bring the interface up. We just disallow this until a + * bind. + */ + +static int shaper_open(struct device *dev) +{ + struct shaper *shaper=dev->priv; + + /* + * Can't open until attached. + * Also can't open until speed is set, or we'll get + * a division by zero. + */ + + if(shaper->dev==NULL) + return -ENODEV; + if(shaper->bitspersec==0) + return -EINVAL; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * Closing a shaper flushes the queues. + */ + +static int shaper_close(struct device *dev) +{ + struct shaper *shaper=dev->priv; + shaper_flush(shaper); + del_timer(&shaper->timer); + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Revectored calls. We alter the parameters and call the functions + * for our attached device. This enables us to bandwidth allocate after + * ARP and other resolutions and not before. + */ + + +static int shaper_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct shaper *sh=dev->priv; + return shaper_qframe(sh, skb); +} + +static struct enet_statistics *shaper_get_stats(struct device *dev) +{ + return NULL; +} + +static int shaper_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + struct shaper *sh=dev->priv; + if(sh_debug) + printk("Shaper header\n"); + return sh->hard_header(skb,sh->dev,type,daddr,saddr,len); +} + +static int shaper_rebuild_header(void *eth, struct device *dev, unsigned long raddr, struct sk_buff *skb) +{ + struct shaper *sh=dev->priv; + if(sh_debug) + printk("Shaper rebuild header\n"); + return sh->rebuild_header(eth,sh->dev,raddr,skb); +} + +static int shaper_attach(struct device *shdev, struct shaper *sh, struct device *dev) +{ + sh->dev = dev; + sh->hard_start_xmit=dev->hard_start_xmit; + sh->get_stats=dev->get_stats; + if(dev->hard_header) + { + sh->hard_header=dev->hard_header; + shdev->hard_header = shaper_header; + } + else + shdev->hard_header = NULL; + + if(dev->rebuild_header) + { + sh->rebuild_header = dev->rebuild_header; + shdev->rebuild_header = shaper_rebuild_header; + } + else + shdev->rebuild_header = NULL; + + shdev->hard_header_len=dev->hard_header_len; + shdev->type=dev->type; + shdev->addr_len=dev->addr_len; + shdev->mtu=dev->mtu; + sh->bitspersec=0; + return 0; +} + +static int shaper_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct shaperconf *ss= (struct shaperconf *)&ifr->ifr_data; + struct shaper *sh=dev->priv; + switch(ss->ss_cmd) + { + case SHAPER_SET_DEV: + { + struct device *them=dev_get(ss->ss_name); + if(them==NULL) + return -ENODEV; + if(sh->dev) + return -EBUSY; + return shaper_attach(dev,dev->priv, them); + } + case SHAPER_GET_DEV: + if(sh->dev==NULL) + return -ENODEV; + strcpy(ss->ss_name, sh->dev->name); + return 0; + case SHAPER_SET_SPEED: + shaper_setspeed(sh,ss->ss_speed); + return 0; + case SHAPER_GET_SPEED: + ss->ss_speed=sh->bitspersec; + return 0; + default: + return -EINVAL; + } +} + +static struct shaper *shaper_alloc(struct device *dev) +{ + struct shaper *sh=kmalloc(sizeof(struct shaper), GFP_KERNEL); + if(sh==NULL) + return NULL; + memset(sh,0,sizeof(*sh)); + skb_queue_head_init(&sh->sendq); + init_timer(&sh->timer); + sh->timer.function=shaper_timer; + sh->timer.data=(unsigned long)sh; + return sh; +} + +/* + * Add a shaper device to the system + */ + +int shaper_probe(struct device *dev) +{ + int i; + + /* + * Set up the shaper. + */ + + dev->priv = shaper_alloc(dev); + if(dev->priv==NULL) + return -ENOMEM; + + dev->open = shaper_open; + dev->stop = shaper_close; + dev->hard_start_xmit = shaper_start_xmit; + dev->get_stats = shaper_get_stats; + dev->set_multicast_list = NULL; + + /* + * Intialise the packet queues + */ + + for(i=0;ibuffs[i]); + + /* + * Handlers for when we attach to a device. + */ + + dev->hard_header = shaper_header; + dev->rebuild_header = shaper_rebuild_header; + dev->do_ioctl = shaper_ioctl; + dev->hard_header_len = 0; + dev->type = ARPHRD_ETHER; /* initially */ + dev->set_mac_address = NULL; + dev->mtu = 1500; + dev->addr_len = 0; + dev->tx_queue_len = 10; + dev->flags = 0; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = 4; + + /* + * Shaper is ok + */ + + return 0; +} + +#ifdef MODULE + +static char devicename[9]; + +static struct device dev_shape = +{ + devicename, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + +int init_module(void) +{ + int i; + for(i=0;i<99;i++) + { + sprintf(devicename,"shaper%d",i); + if(dev_get(devicename)==NULL) + break; + } + if(i==100) + return -ENFILE; + + printk(SHAPER_BANNER); + if (register_netdev(&dev_shape) != 0) + return -EIO; + printk("Traffic shaper initialised.\n"); + return 0; +} + +void cleanup_module(void) +{ + /* + * No need to check MOD_IN_USE, as sys_delete_module() checks. + * To be unloadable we must be closed and detached so we don't + * need to flush things. + */ + + unregister_netdev(&dev_shape); + + /* + * Free up the private structure, or leak memory :-) + */ + + kfree(dev_shape.priv); + dev_shape.priv = NULL; +} + +#else + +static struct device dev_sh0 = +{ + "shaper0", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + + +static struct device dev_sh1 = +{ + "shaper1", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + + +static struct device dev_sh2 = +{ + "shaper2", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + +static struct device dev_sh3 = +{ + "shaper3", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, shaper_probe +}; + +void shaper_init(void) +{ + register_netdev(&dev_sh0); + register_netdev(&dev_sh1); + register_netdev(&dev_sh2); + register_netdev(&dev_sh3); +} + +#endif /* MODULE */ diff -u --recursive --new-file v2.0.35/linux/drivers/net/strip.c linux/drivers/net/strip.c --- v2.0.35/linux/drivers/net/strip.c Sun Nov 15 10:49:40 1998 +++ linux/drivers/net/strip.c Sun Nov 15 10:33:03 1998 @@ -81,8 +81,6 @@ /************************************************************************/ /* Header files */ -#include - #ifdef MODULE #include #include @@ -1714,7 +1712,7 @@ { struct strip *strip_info = (struct strip *)(dev->priv); STRIP_Header *header = (STRIP_Header *)buff; - /* Arp find returns zero if if knows the address, */ + /* Arp find returns zero if it knows the address, */ /* or if it doesn't know the address it sends an ARP packet and returns non-zero */ int arp_result = arp_find(header->dst_addr.c, dst, dev, dev->pa_addr, skb); diff -u --recursive --new-file v2.0.35/linux/drivers/net/tlan.c linux/drivers/net/tlan.c --- v2.0.35/linux/drivers/net/tlan.c Sun Nov 15 10:49:40 1998 +++ linux/drivers/net/tlan.c Sun Nov 15 10:33:04 1998 @@ -6,6 +6,7 @@ * by James Banks * * (C) 1997-1998 Caldera, Inc. + * (C) 1998 James Banks * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. @@ -58,8 +59,8 @@ static int speed = 0; static u8 *TLanPadBuffer; static char TLanSignature[] = "TLAN"; -static int TLanVersionMajor = 0; -static int TLanVersionMinor = 43; +static int TLanVersionMajor = 1; +static int TLanVersionMinor = 0; static TLanAdapterEntry TLanAdapterList[] = { @@ -582,10 +583,7 @@ if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) { pcibios_write_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER ); - printk( "TLAN: Attempting to activate busmastering.\n" ); - printk( "TLAN: You may need to set busmastering to on in the CMOS\n" ); - printk( "TLAN: before this card will work.\n" ); - *pci_io_base = 0; + printk( "TLAN: Activating PCI bus mastering for this device.\n" ); } pci_index++; @@ -1222,7 +1220,7 @@ * of the list. If the frame was the last in the Rx * channel (EOC), the function restarts the receive channel * by sending an Rx Go command to the adapter. Then it - * activates/continues the the activity LED. + * activates/continues the activity LED. * **************************************************************/ @@ -1723,7 +1721,7 @@ * io_base Base IO port of the device of * which to print DIO registers. * - * This function prints out all the the internal (DIO) + * This function prints out all the internal (DIO) * registers of a TLAN chip. * **************************************************************/ diff -u --recursive --new-file v2.0.35/linux/drivers/net/tulip.c linux/drivers/net/tulip.c --- v2.0.35/linux/drivers/net/tulip.c Mon Jul 13 13:46:30 1998 +++ linux/drivers/net/tulip.c Sun Nov 15 10:33:04 1998 @@ -1,12 +1,13 @@ -/* tulip.c: A DEC 21040-family ethernet driver for linux. */ +/* tulip.c: A DEC 21040-family ethernet driver for Linux. */ /* Written 1994-1998 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - This driver is for the SMC EtherPower PCI ethernet adapter. - It should work with most other DEC 21*40-based ethercards. + This driver is for the Digital "Tulip" ethernet adapter interface. + It should work with most DEC 21*4*-based chips/ethercards, as well as + PNIC and MXIC chips. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences @@ -17,7 +18,7 @@ */ #define SMP_CHECK -static const char version[] = "tulip.c:v0.88 4/7/98 becker@cesdis.gsfc.nasa.gov\n"; +static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ @@ -30,6 +31,14 @@ static int options[MAX_UNITS] = {0, }; static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */ +/* The possible media types that can be set in options[] are: */ +static const char * const medianame[] = { + "10baseT", "10base2", "AUI", "100baseTx", + "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", + "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", + "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", +}; + /* Set if the PCI BIOS detects the chips on a multiport board backwards. */ #ifdef REVERSE_PROBE_ORDER static int reverse_probe = 1; @@ -51,18 +60,9 @@ static int rx_copybreak = 100; #endif -/* The following example shows how to always use the 10base2 port. */ -#ifdef notdef -#define TULIP_DEFAULT_MEDIA 1 /* 1 == 10base2 */ -#define TULIP_NO_MEDIA_SWITCH /* Don't switch from this port */ -#endif - -/* Define to force full-duplex operation on all Tulip interfaces. */ -/* #define TULIP_FULL_DUPLEX 1 */ - /* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((2000*HZ)/1000) +#define TX_TIMEOUT (4*HZ) #include #ifdef MODULE @@ -272,8 +272,6 @@ #ifndef PCI_VENDOR_ID_LITEON #define PCI_VENDOR_ID_LITEON 0x11AD #endif -#define PCI_DEVICE_ID_PNIC 0x0002 -#define PCI_DEVICE_ID_PNIC_X 0x0168 #ifndef PCI_VENDOR_ID_MXIC #define PCI_VENDOR_ID_MXIC 0x10d9 @@ -290,44 +288,44 @@ static void pnic_timer(unsigned long data); /* A table describing the chip types. */ -enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2}; +enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,}; static struct tulip_chip_table { - int device_id; + int vendor_id, device_id; char *chip_name; int io_size; int valid_intrs; /* CSR7 interrupt enable settings */ int flags; void (*media_timer)(unsigned long data); } tulip_tbl[] = { - { PCI_DEVICE_ID_DEC_TULIP, "Digital DC21040 Tulip", 128, 0x0001ebef, - 0, tulip_timer }, - { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DC21041 Tulip", 128, 0x0001ebef, - HAS_MEDIA_TABLE, tulip_timer }, - { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE, tulip_timer }, - { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 256, 0x0801fbff, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, + "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, + "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST, + "Digital DS21140 Tulip", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, + tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142, + "Digital DS21142/3 Tulip", 256, 0x0801fbff, HAS_MII | HAS_MEDIA_TABLE, t21142_timer }, - { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 256, 0x0001ebef, - 0, pnic_timer }, - { PCI_DEVICE_ID_MX98713, "Macronix 98713 PMAC", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE, tulip_timer /* Tulip-like! */ }, - { PCI_DEVICE_ID_MX98715, "Macronix 98715 PMAC", 256, 0x0001ebef, - HAS_MEDIA_TABLE, mxic_timer }, - { PCI_DEVICE_ID_MX98725, "Macronix 98725 PMAC", 256, 0x0001ebef, - HAS_MEDIA_TABLE, mxic_timer }, + { PCI_VENDOR_ID_LITEON, 0x0002, + "Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713, + "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715, + "Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725, + "Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, + { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, {0, 0, 0, 0}, }; /* This matches the table above. */ enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, LC82C168, MX98713, MX98715, MX98725}; -static const char * const medianame[] = { - "10baseT", "10base2", "AUI", "100baseTx", - "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", - "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", - "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", -}; -/* A full-duplex map for above. */ +/* A full-duplex map for media types. */ enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, MediaIs100=16}; static const char media_cap[] = @@ -378,7 +376,7 @@ struct mediatable { u16 defaultmedia; u8 leafcount, csr12dir; /* General purpose pin directions. */ - unsigned has_mii:1; + unsigned has_mii:1, has_nonmii:1; struct medialeaf mleaf[0]; }; @@ -418,6 +416,7 @@ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ unsigned int full_duplex_lock:1; + unsigned int fake_addr:1; /* Multiport board faked address. */ unsigned int default_port:4; /* Last dev->if_port value. */ unsigned int media2:4; /* Secondary monitored media port. */ unsigned int medialock:1; /* Don't sense media type. */ @@ -434,12 +433,12 @@ }; static struct device *tulip_probe1(int pci_bus, int pci_devfn, - struct device *dev, int ioaddr, + struct device *dev, int chip_id, int options); static void parse_eeprom(struct device *dev); -static int read_eeprom(int ioaddr, int location); -static int mdio_read(int ioaddr, int phy_id, int location); -static void mdio_write(int ioaddr, int phy_id, int location, int value); +static int read_eeprom(long ioaddr, int location); +static int mdio_read(struct device *dev, int phy_id, int location); +static void mdio_write(struct device *dev, int phy_id, int location, int value); static void select_media(struct device *dev, int startup); static int tulip_open(struct device *dev); static void tulip_timer(unsigned long data); @@ -474,98 +473,100 @@ { int cards_found = 0; static int pci_index = 0; /* Static, for multiple probe calls. */ + unsigned char pci_bus, pci_device_fn; /* Ideally we would detect all network cards in slot order. That would be best done a central PCI probe dispatch, which wouldn't work well with the current structure. So instead we detect just the Tulip cards in slot order. */ - if (pcibios_present()) { - unsigned char pci_bus, pci_device_fn; - - for (;pci_index < 0xff; pci_index++) { - u16 vendor, device, pci_command, new_command; - u32 pci_ioaddr; - int chip_idx = 0; - - if (pcibios_find_class - (PCI_CLASS_NETWORK_ETHERNET << 8, - reverse_probe ? 0xfe - pci_index : pci_index, - &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) - if (reverse_probe) - continue; - else - break; - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_DEVICE_ID, &device); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &pci_ioaddr); - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; - - if (vendor != PCI_VENDOR_ID_DEC - && vendor != PCI_VENDOR_ID_LITEON - && vendor != PCI_VENDOR_ID_MXIC) +#if LINUX_VERSION_CODE >= 0x20155 + if (! pci_present()) + return -ENODEV; +#else + if (! pcibios_present()) + return -ENODEV; +#endif + for (;pci_index < 0xff; pci_index++) { + u16 vendor, device, pci_command, new_command; + u32 pci_ioaddr; + int chip_idx = 0; + + if (pcibios_find_class + (PCI_CLASS_NETWORK_ETHERNET << 8, + reverse_probe ? 0xfe - pci_index : pci_index, + &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) + if (reverse_probe) continue; - if (vendor == PCI_VENDOR_ID_LITEON) - device = PCI_DEVICE_ID_PNIC_X; - else if (vendor == PCI_VENDOR_ID_MXIC) - device = PCI_DEVICE_ID_MX98713; - - for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) - if (device == tulip_tbl[chip_idx].device_id) - break; - if (tulip_tbl[chip_idx].chip_name == 0) { + else + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + + for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) + if (vendor == tulip_tbl[chip_idx].vendor_id && + device == tulip_tbl[chip_idx].device_id) + break; + if (tulip_tbl[chip_idx].chip_name == 0) { + if (vendor == PCI_VENDOR_ID_DEC || + vendor == PCI_VENDOR_ID_LITEON) printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type" " %4.4x %4.4x"" detected: not configured.\n", vendor, device); - continue; - } - if (tulip_debug > 2) - printk(KERN_DEBUG "Found %s at I/O %#x.\n", - tulip_tbl[chip_idx].chip_name, pci_ioaddr); + continue; + } +#if LINUX_VERSION_CODE >= 0x20155 + pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; +#else + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, + &pci_ioaddr); +#endif + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; - if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size)) - continue; + if (tulip_debug > 2) + printk(KERN_DEBUG "Found %s at I/O %#x.\n", + tulip_tbl[chip_idx].chip_name, pci_ioaddr); - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled this" - " device! Updating PCI command %4.4x->%4.4x.\n", - pci_command, new_command); - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } - - dev = tulip_probe1(pci_bus, pci_device_fn, dev, - pci_ioaddr, chip_idx, cards_found); - - /* Get and check the bus-master and latency values. */ - if (dev) { - unsigned char pci_latency; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 10) { - printk(KERN_INFO " PCI latency timer (CFLT) is " - "unreasonably low at %d. Setting to 64 clocks.\n", - pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 64); - } else if (tulip_debug > 1) - printk(KERN_INFO " PCI latency timer (CFLT) is %#x, " - " PCI command is %4.4x.\n", - pci_latency, new_command); - /* Bring the 21143 out power-down mode. */ - if (device == PCI_DEVICE_ID_DEC_TULIP_21142) - pcibios_write_config_dword(pci_bus, pci_device_fn, - 0x40, 0x40000000); - dev = 0; - cards_found++; - } + if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size)) + continue; + + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled this" + " device! Updating PCI command %4.4x->%4.4x.\n", + pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found); + + /* Get and check the bus-master and latency values. */ + if (dev) { + unsigned char pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 10) { + printk(KERN_INFO " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to 64 clocks.\n", + pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 64); + } else if (tulip_debug > 1) + printk(KERN_INFO " PCI latency timer (CFLT) is %#x, " + " PCI command is %4.4x.\n", + pci_latency, new_command); + /* Bring the 21143 out power-down mode. */ + if (device == PCI_DEVICE_ID_DEC_TULIP_21142) + pcibios_write_config_dword(pci_bus, pci_device_fn, + 0x40, 0x40000000); + dev = 0; + cards_found++; } } @@ -573,15 +574,17 @@ } static struct device *tulip_probe1(int pci_bus, int pci_device_fn, - struct device *dev, int ioaddr, + struct device *dev, int chip_id, int board_idx) { static int did_version = 0; /* Already printed version info. */ struct tulip_private *tp; + long ioaddr; + int irq; /* See note below on the multiport cards. */ static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; static int last_irq = 0; - u8 pci_irq_line; + static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */ int i; unsigned short sum; @@ -590,9 +593,25 @@ dev = init_etherdev(dev, 0); - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - printk(KERN_INFO "%s: %s at %#3x,", +#if LINUX_VERSION_CODE >= 0x20155 + irq = pci_find_slot(pci_bus, pci_device_fn)->irq; + ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; +#else + { + u8 pci_irq_line; + u32 pci_ioaddr; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, + &pci_ioaddr); + irq = pci_irq_line; + ioaddr = pci_ioaddr; + } +#endif + /* Remove I/O space marker in bit 0. */ + ioaddr &= ~3; + + printk(KERN_INFO "%s: %s at %#3lx,", dev->name, tulip_tbl[chip_id].chip_name, ioaddr); /* Stop the chip's Tx and Rx processes. */ @@ -649,6 +668,10 @@ for (i = 0; i < 8; i ++) if (ee_data[i] != ee_data[16+i]) sa_offset = 20; + if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { + sa_offset = 2; /* Grrr, damn Matrox boards. */ + multiport_cnt = 4; + } for (i = 0; i < 6; i ++) { dev->dev_addr[i] = ee_data[i + sa_offset]; sum += ee_data[i + sa_offset]; @@ -672,20 +695,21 @@ dev->dev_addr[i] = last_phys_addr[i]; dev->dev_addr[i] = last_phys_addr[i] + 1; #if defined(__i386__) /* This BIOS bug doesn't exist on Alphas. */ - pci_irq_line = last_irq; + irq = last_irq; #endif } + for (i = 0; i < 6; i++) printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]); - printk(", IRQ %d.\n", pci_irq_line); - last_irq = pci_irq_line; + printk(", IRQ %d.\n", irq); + last_irq = irq; /* We do a request_region() only to register /proc/ioports info. */ /* Note that proper size is tulip_tbl[chip_id].chip_name, but... */ - request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name); + request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name); dev->base_addr = ioaddr; - dev->irq = pci_irq_line; + dev->irq = irq; /* Make certain the data structures are quadword aligned. */ tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); @@ -746,9 +770,9 @@ but takes much time. */ for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(ioaddr, phy, 1); + int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { - int mii_reg0 = mdio_read(ioaddr, phy, 0); + int mii_reg0 = mdio_read(dev, phy, 0); int reg4 = ((mii_status>>6) & tp->to_advertise) | 1; tp->phys[phy_idx] = phy; tp->advertising[phy_idx++] = reg4; @@ -758,11 +782,11 @@ if (1 || (media_cap[tp->default_port] & MediaIsMII)) { printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d," " previously advertising %4.4x.\n", - dev->name, reg4, phy, mdio_read(ioaddr, phy, 4)); - mdio_write(ioaddr, phy, 4, reg4); + dev->name, reg4, phy, mdio_read(dev, phy, 4)); + mdio_write(dev, phy, 4, reg4); } /* Enable autonegotiation: some boards default to off. */ - mdio_write(ioaddr, phy, 0, mii_reg0 | + mdio_write(dev, phy, 0, mii_reg0 | (tp->full_duplex ? 0x1100 : 0x1000) | (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0)); } @@ -813,10 +837,12 @@ outl(0x1301, ioaddr + CSR12); /* Start NWay. */ break; case LC82C168: - outl(0x00420000, ioaddr + CSR6); - outl(0x30, ioaddr + CSR12); - outl(0x0001F078, ioaddr + 0xB8); - outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + if ( ! tp->mii_cnt) { + outl(0x00420000, ioaddr + CSR6); + outl(0x30, ioaddr + CSR12); + outl(0x0001F078, ioaddr + 0xB8); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + } break; case MX98713: case MX98715: case MX98725: outl(0x00000000, ioaddr + CSR6); @@ -846,7 +872,9 @@ {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, 0x0000, 0x009E, /* 10baseT */ 0x0903, 0x006D, /* 100baseTx */ }}, - {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f, + 0x0107, 0x8021, /* 100baseFx */ + 0x0108, 0x8021, /* 100baseFx-FD */ 0x0103, 0x006D, /* 100baseTx */ }}, {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ @@ -876,7 +904,7 @@ static unsigned char *last_ee_data = NULL; static int controller_index = 0; struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; unsigned char *ee_data = tp->eeprom; int i; @@ -956,8 +984,9 @@ for (i = 0; i < count; i++) { unsigned char media_code = *p++; u16 csrvals[3]; - for (i = 0; i < 3; i++) { - csrvals[i] = get_u16(p); + int idx; + for (idx = 0; idx < 3; idx++) { + csrvals[idx] = get_u16(p); p += 2; } if (media_code & 0x40) { @@ -977,7 +1006,7 @@ u16 media = get_u16(p); p += 2; - if (tp->chip_id == DC21140 || tp->chip_id == MX98713) + if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM) csr12dir = *p++; count = *p++; mtable = (struct mediatable *) @@ -989,7 +1018,7 @@ mtable->defaultmedia = media; mtable->leafcount = count; mtable->csr12dir = csr12dir; - mtable->has_mii = 0; + mtable->has_nonmii = mtable->has_mii = 0; printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name, media & 0x0800 ? "Autosense" : medianame[media & 15]); @@ -1008,8 +1037,10 @@ if (p[1] & 1) { mtable->has_mii = 1; leaf->media = 11; - } else + } else { + mtable->has_nonmii = 1; leaf->media = p[2] & 0x0f; + } leaf->leafdata = p + 2; p += (p[0] & 0x3f) + 1; } @@ -1052,11 +1083,11 @@ #define EE_READ_CMD (6 << 6) #define EE_ERASE_CMD (7 << 6) -static int read_eeprom(int ioaddr, int location) +static int read_eeprom(long ioaddr, int location) { int i; unsigned short retval = 0; - int ee_addr = ioaddr + CSR9; + long ee_addr = ioaddr + CSR9; int read_cmd = location | EE_READ_CMD; outl(EE_ENB & ~EE_CS, ee_addr); @@ -1107,12 +1138,23 @@ #define MDIO_ENB_IN 0x40000 #define MDIO_DATA_READ 0x80000 -static int mdio_read(int ioaddr, int phy_id, int location) +static int mdio_read(struct device *dev, int phy_id, int location) { + struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; int retval = 0; - int mdio_addr = ioaddr + CSR9; + long mdio_addr = dev->base_addr + CSR9; + + if (tp->chip_id == LC82C168) { + long ioaddr = dev->base_addr; + int i = 1000; + outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + while (--i > 0) + if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) + return retval & 0xffff; + return 0xffff; + } /* Establish sync by sending at least 32 logic ones. */ for (i = 32; i >= 0; i--) { @@ -1141,11 +1183,23 @@ return (retval>>1) & 0xffff; } -static void mdio_write(int ioaddr, int phy_id, int location, int value) +static void mdio_write(struct device *dev, int phy_id, int location, int value) { + struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; - int mdio_addr = ioaddr + CSR9; + long mdio_addr = dev->base_addr + CSR9; + + if (tp->chip_id == LC82C168) { + long ioaddr = dev->base_addr; + int i = 1000; + outl(cmd, ioaddr + 0xA0); + do + if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) + break; + while (--i > 0); + return; + } /* Establish sync by sending 32 logic ones. */ for (i = 32; i >= 0; i--) { @@ -1177,7 +1231,7 @@ tulip_open(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int i = 0; /* On some chip revs we must set the MII/SYM port before the reset!? */ @@ -1286,11 +1340,11 @@ int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 : (dev->if_port == 12 ? 0 : dev->if_port); for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == looking_for) { - printk(KERN_INFO "%s: Using user-specified media %s.\n", - dev->name, medianame[dev->if_port]); - goto media_picked; - } + if (tp->mtable->mleaf[i].media == looking_for) { + printk(KERN_INFO "%s: Using user-specified media %s.\n", + dev->name, medianame[dev->if_port]); + goto media_picked; + } } if ((tp->mtable->defaultmedia & 0x0800) == 0) for (i = 0; i < tp->mtable->leafcount; i++) @@ -1313,6 +1367,10 @@ outl(0x0008, ioaddr + CSR15); outl(0x0001, ioaddr + CSR13); outl(0x1301, ioaddr + CSR12); + } else if (tp->chip_id == LC82C168 && tp->mii_cnt && ! tp->medialock) { + dev->if_port = 11; + tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0001, ioaddr + CSR15); } else select_media(dev, 1); @@ -1325,14 +1383,15 @@ dev->start = 1; /* Enable interrupts by setting the interrupt mask. */ + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); outl(tp->csr6 | 0x2002, ioaddr + CSR6); outl(0, ioaddr + CSR2); /* Rx poll demand */ if (tulip_debug > 2) { - printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n", + printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), - inl(ioaddr + CSR13)); + inl(ioaddr + CSR6)); } /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ @@ -1348,7 +1407,7 @@ /* Set up the transceiver control registers for the selected media type. */ static void select_media(struct device *dev, int startup) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; struct tulip_private *tp = (struct tulip_private *)dev->priv; struct mediatable *mtable = tp->mtable; u32 new_csr6; @@ -1441,7 +1500,7 @@ printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n", dev->name, to_advertise, phy_num, tp->phys[phy_num]); /* Bogus: put in by a committee? */ - mdio_write(ioaddr, tp->phys[phy_num], 4, to_advertise); + mdio_write(dev, tp->phys[phy_num], 4, to_advertise); break; } default: @@ -1462,14 +1521,18 @@ outl(t21041_csr13[dev->if_port], ioaddr + CSR13); new_csr6 = 0x80020000; } else if (tp->chip_id == LC82C168) { - if (startup) - dev->if_port = 0; + if (startup && ! tp->medialock) + dev->if_port = tp->mii_cnt ? 11 : 0; if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LiteOn PHY status is %3.3x, CSR12 %4.4x," + printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x," " media %s.\n", dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), medianame[dev->if_port]); - if (startup) { + if (tp->mii_cnt) { + new_csr6 = 0x812C0000; + outl(0x0001, ioaddr + CSR15); + outl(0x0201B07A, ioaddr + 0xB8); + } else if (startup) { /* Start with 10mbps to do autonegotiation. */ outl(0x32, ioaddr + CSR12); new_csr6 = 0x00420000; @@ -1525,7 +1588,7 @@ { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; u32 csr12 = inl(ioaddr + CSR12); int next_tick = 0; @@ -1661,22 +1724,22 @@ case 1: case 3: { /* 21140, 21142 MII */ int mii_reg1, mii_reg5; actually_mii: - mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1); - mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5); + mii_reg1 = mdio_read(dev, tp->phys[0], 1); + mii_reg5 = mdio_read(dev, tp->phys[0], 5); if (tulip_debug > 1) printk(KERN_INFO "%s: MII status %4.4x, Link partner report " "%4.4x, CSR12 %2.2x, %cD.\n", dev->name, mii_reg1, mii_reg5, csr12, tp->full_duplex ? 'F' : 'H'); if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) { - int new_reg1 = mdio_read(ioaddr, tp->phys[0], 1); - if ((new_reg1 & 0x0004) == 0) + int new_reg1 = mdio_read(dev, tp->phys[0], 1); + if ((new_reg1 & 0x0004) == 0) { printk(KERN_INFO "%s: No link beat on the MII interface," " status then %4.4x now %4.4x.\n", dev->name, mii_reg1, new_reg1); -#ifdef notyet - goto select_next_media; -#endif + if (tp->mtable && tp->mtable->has_nonmii) + goto select_next_media; + } } if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) ; /* No MII device or no link partner report */ @@ -1724,7 +1787,7 @@ { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int csr12 = inl(ioaddr + CSR12); int next_tick = 60*HZ; int new_csr6 = 0; @@ -1788,7 +1851,7 @@ static void t21142_lnk_change( struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int csr12 = inl(ioaddr + CSR12); if (tulip_debug > 1) @@ -1839,7 +1902,7 @@ { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int next_tick = 60*HZ; if (tulip_debug > 3) { @@ -1856,50 +1919,82 @@ { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int csr12 = inl(ioaddr + CSR12); - int phy_reg = inl(ioaddr + 0xB8); int next_tick = 60*HZ; - int duplex; + int new_csr6 = tp->csr6 & ~0x40C40200; - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n", - dev->name, phy_reg, inl(ioaddr + CSR5)); - if (tp->full_duplex_lock) - return; /* Do not bother to set timer. */ - duplex = phy_reg & 0x30000000 ? 1 : 0; - if (tp->full_duplex != duplex) { - tp->full_duplex = duplex; - if (tp->full_duplex) - tp->csr6 |= 0x0200; + if (media_cap[dev->if_port] & MediaIsMII) { + int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0]; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 negotiated capability %8.8x, " + "CSR5 %8.8x.\n", + dev->name, negotiated, inl(ioaddr + CSR5)); + + if (negotiated & 0x0380) /* 10 vs 100mbps */ + new_csr6 |= 0x812E0000; else - tp->csr6 &= ~0x0200; - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - if (tulip_debug > 0) /* Gurppp, should be >1 */ - printk(KERN_INFO "%s: Setting %s-duplex based on" - " PNIC PHY report of %8.8x.\n", - dev->name, tp->full_duplex ? "full" : "half", phy_reg); - } - if (phy_reg & 0x04000000) { /* Remote link fault */ - /*outl(0x0201F078, ioaddr + 0xB8);*/ - next_tick = 3*HZ; - } - if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ + new_csr6 |= 0x816E0000; + if (((negotiated & 0x0300) == 0x0100) /* Duplex */ + || (negotiated & 0x00C0) == 0x0040 + || tp->full_duplex_lock) { + tp->full_duplex = 1; + new_csr6 |= 0x0200; + } if (tulip_debug > 1) - printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " - "CSR5 %8.8x, PHY %3.3x.\n", - dev->name, medianame[dev->if_port], csr12, - inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); - if (dev->if_port == 0) { - dev->if_port = 3; + printk(KERN_DEBUG "%s: LC82C168 MII PHY status %4.4x, Link " + "partner report %4.4x, csr6 %8.8x/%8.8x.\n", + dev->name, mdio_read(dev, tp->phys[0], 1), negotiated, + tp->csr6, inl(ioaddr + CSR6)); + } else { + int phy_reg = inl(ioaddr + 0xB8); + int csr5 = inl(ioaddr + CSR5); + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n", + dev->name, phy_reg, csr5); + + if (phy_reg & 0x04000000) { /* Remote link fault */ + /*outl(0x0201F078, ioaddr + 0xB8);*/ + next_tick = 3*HZ; + } + if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " + "CSR5 %8.8x, PHY %3.3x.\n", + dev->name, medianame[dev->if_port], csr12, + inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); + if (tp->medialock) { + } else if (dev->if_port == 0) { + dev->if_port = 3; + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + outl(0x1F868, ioaddr + 0xB8); + } else { + dev->if_port = 0; + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); + } + new_csr6 |= (tp->csr6 & 0xfdff); + next_tick = 3*HZ; } else - dev->if_port = 0; - next_tick = 3*HZ; - select_media(dev, 0); - outl(tp->csr6 | 0x0002, ioaddr + CSR6); + new_csr6 = tp->csr6; + if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) { + tp->full_duplex = 1; + new_csr6 |= 0x00000200; + } + } + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ outl(tp->csr6 | 0x2002, ioaddr + CSR6); dev->trans_start = jiffies; + if (tulip_debug > 0) /* Gurppp, should be >1 */ + printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, " + "CSR6 %8.8x.\n", + dev->name, tp->full_duplex ? "full" : "half", new_csr6); } tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer); @@ -1908,13 +2003,15 @@ static void tulip_tx_timeout(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; - if (tp->mtable && tp->mtable->has_mii) { - /* Do nothing -- the media monitor should handle this. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", - dev->name); + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + dev->trans_start = jiffies; + return; } else if (tp->chip_id == DC21040) { if (inl(ioaddr + CSR12) & 0x0002) { printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n", @@ -2102,7 +2199,8 @@ #endif struct tulip_private *tp; - int csr5, ioaddr, work_budget = max_interrupt_work; + long ioaddr; + int csr5, work_budget = max_interrupt_work; if (dev == NULL) { printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", @@ -2119,8 +2217,8 @@ tp->smp_proc_id, hard_smp_processor_id()); #else printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); - return; #endif + return; } dev->interrupt = 1; #ifdef SMP_CHECK @@ -2210,6 +2308,14 @@ } tp->dirty_tx = dirty_tx; + if (csr5 & TxDied) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: The transmitter stopped!" + " CSR5 is %x, CSR6 %x.\n", + dev->name, csr5, inl(ioaddr + CSR6)); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } } /* Log errors. */ @@ -2261,24 +2367,6 @@ printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); -#ifdef notdef - /* Code that should never be run! Perhaps remove after testing.. */ - { - static int stopit = 10; - if (dev->start == 0 && --stopit < 0) { - printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n" - KERN_ERR "%s: Disabling interrupt handler %d to avoid " - "locking up the machine.\n", - dev->name, dev->name, dev->irq); -#ifdef SA_SHIRQ - free_irq(irq, dev); -#else - free_irq(irq); -#endif - } - } -#endif - dev->interrupt = 0; clear_bit(0, (void*)&tp->interrupt); return; @@ -2311,6 +2399,9 @@ } } else if (status & 0x8000) { /* There was a fatal error. */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); tp->stats.rx_errors++; /* end of a packet.*/ if (status & 0x0890) tp->stats.rx_length_errors++; if (status & 0x0004) tp->stats.rx_frame_errors++; @@ -2318,7 +2409,7 @@ if (status & 0x0001) tp->stats.rx_fifo_errors++; } else { /* Omit the four octet CRC from the length. */ - short pkt_len = (status >> 16) - 4; + short pkt_len = ((status >> 16) & 0x7FF) - 4; struct sk_buff *skb; /* Check if the packet is long enough to just accept without @@ -2396,7 +2487,7 @@ static int tulip_close(struct device *dev) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; @@ -2464,7 +2555,7 @@ tulip_get_stats(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; if (dev->start) tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; @@ -2514,7 +2605,7 @@ } else { save_flags(flags); cli(); - data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); restore_flags(flags); } return 0; @@ -2525,7 +2616,7 @@ } else { save_flags(flags); cli(); - mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); restore_flags(flags); } return 0; @@ -2569,7 +2660,7 @@ static void set_rx_mode(struct device *dev, int num_addrs, void *addrs) #endif { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int csr6 = inl(ioaddr + CSR6) & ~0x00D5; struct tulip_private *tp = (struct tulip_private *)dev->priv; @@ -2686,7 +2777,7 @@ pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); io &= ~3; - dev = tulip_probe1(bus, devfn, NULL, io, DC21142, -1); + dev = tulip_probe1(bus, devfn, NULL, DC21142, -1); if (dev) { dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); strcpy(node->dev_name, dev->name); diff -u --recursive --new-file v2.0.35/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c --- v2.0.35/linux/drivers/net/via-rhine.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/via-rhine.c Sun Nov 15 10:33:04 1998 @@ -0,0 +1,1317 @@ +/* via-rhine.c: A Linux Ethernet device driver for VIA Rhine family chips. */ +/* + Written 1998 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + Drivers derived from this code also fall under the GPL and must retain + this authorship and copyright notice. + + This driver is designed for the VIA VT86c100A Rhine-II PCI Fast Ethernet + controller. It also works with the older 3043 Rhine-I chip. + + The author may be reached as becker@cesdis.edu, or + Donald Becker + 312 Severn Ave. #W302 + Annapolis MD 21403 + + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html +*/ + +static const char *versionA = +"via-rhine.c:v1.00 9/5/98 Written by Donald Becker\n"; +static const char *versionB = +" http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n"; + +/* A few user-configurable values. These may be modified when a driver + module is loaded.*/ + +static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ +static int max_interrupt_work = 20; +static int min_pci_latency = 64; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. + Both 'options[]' and 'full_duplex[]' should exist for driver + interoperability. + The media type is usually passed in 'options[]'. +*/ +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). + The Rhine has a 64 element 8390-like hash table. */ +static const int multicast_filter_limit = 32; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. + The compiler will convert '%'<2^N> into a bit mask. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 8 +#define RX_RING_SIZE 16 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include + +/* This driver was written to use PCI memory space, however some boards + only work with I/O space accesses. */ +#define VIA_USE_IO +#ifdef VIA_USE_IO +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +/* Kernel compatibility defines, some common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ + +#define RUN_AT(x) (jiffies + (x)) + +#if (LINUX_VERSION_CODE >= 0x20100) +char kernel_version[] = UTS_RELEASE; +#else +#ifndef __alpha__ +#define ioremap vremap +#define iounmap vfree +#endif +#endif +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(min_pci_latency, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +#if LINUX_VERSION_CODE < 0x20123 +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#else +#define NETSTATS_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) +/* Grrrr, the PCI code changed, but did not consider CardBus... */ +#include +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); +#endif + + +/* + Theory of Operation + +I. Board Compatibility + +This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet +controller. + +II. Board-specific settings + +Boards with this chip are functional only in a bus-master PCI slot. + +Many operational settings are loaded from the EEPROM to the Config word at +offset 0x78. This driver assumes that they are correct. +If this driver is compiled to use PCI memory space operations the EEPROM +must be configured to enable memory ops. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. + +IIIb/c. Transmit/Receive Structure + +This driver attempts to use a zero-copy receive and transmit scheme. + +Alas, all data buffers are required to start on a 32 bit boundary, so +the driver must often copy transmit packets into bounce buffers. + +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers. When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack. Buffers consumed this way are replaced by newly allocated +skbuffs in the last phase of netdev_rx(). + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets. When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine. Copying also preloads the cache, which is +most useful with small frames. + +Since the VIA chips are only able to transfer data to buffers on 32 bit +boundaries, the the IP header at offset 14 in an ethernet frame isn't +longword aligned for further processing. Copying these unaligned buffers +has the beneficial effect of 16-byte aligning the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +IVb. References + +Preliminary VT86C100A manual from http://www.via.com.tw/ +http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html + +IVc. Errata + +The VT86C100A manual is not reliable information. +The chip does not handle unaligned transmit or receive buffers, resulting +in significant performance degradation for bounce buffer copies on transmit +and unaligned IP headers on receive. +The chip does not pad to minimum transmit length. + +*/ + + + +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. + Note the matching code -- the first table entry matchs all 56** cards but + second only the 1234 card. +*/ +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; + +static struct device *via_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, int irq, + int chp_idx, int fnd_cnt); + +static struct pci_id_info pci_tbl[] = { + { "VIA VT86C100A Rhine-II", 0x1106, 0x6100, 0xffff, + PCI_USES_MEM|PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1}, + { "VIA VT3043 Rhine", 0x1106, 0x3043, 0xffff, + PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1}, + {0,}, /* 0 terminated list. */ +}; + + +/* A chip capabilities table, matching the entries in pci_tbl[] above. */ +enum chip_capability_flags {CanHaveMII=1, }; +struct chip_info { + int io_size; + int flags; +} static cap_tbl[] = { + {128, CanHaveMII, }, + {128, CanHaveMII, }, +}; + + +/* Offsets to the device registers. +*/ +enum register_offsets { + StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08, + IntrStatus=0x0C, IntrEnable=0x0E, + MulticastFilter0=0x10, MulticastFilter1=0x14, + RxRingPtr=0x18, TxRingPtr=0x1C, + MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIConfig=0x6E, + MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, + Config=0x78, RxMissed=0x7C, RxCRCErrs=0x7E, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { + IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020, + IntrTxDone=0x0002, IntrTxAbort=0x0008, IntrTxUnderrun=0x0010, + IntrPCIErr=0x0040, + IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200, + IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000, + IntrTxAborted=0x2000, IntrLinkChange=0x4000, + IntrRxWakeUp=0x8000, + IntrNormalSummary=0x0003, IntrAbnormalSummary=0x8260, +}; + + +/* The Rx and Tx buffer descriptors. */ +struct rx_desc { + u16 rx_status; + u16 rx_length; + u32 desc_length; + u32 addr; + u32 next_desc; +}; +struct tx_desc { + u16 tx_status; + u16 tx_own; + u32 desc_length; + u32 addr; + u32 next_desc; +}; + +/* Bits in *_desc.status */ +enum rx_status_bits { + RxDescOwn=0x80000000, RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F}; +enum desc_status_bits { + DescOwn=0x8000, DescEndPacket=0x4000, DescIntr=0x1000, +}; + +/* Bits in ChipCmd. */ +enum chip_cmd_bits { + CmdInit=0x0001, CmdStart=0x0002, CmdStop=0x0004, CmdRxOn=0x0008, + CmdTxOn=0x0010, CmdTxDemand=0x0020, CmdRxDemand=0x0040, + CmdEarlyRx=0x0100, CmdEarlyTx=0x0200, CmdFDuplex=0x0400, + CmdNoTxPoll=0x0800, CmdReset=0x8000, +}; + +struct netdev_private { + /* Descriptor rings first for alignment. */ + struct rx_desc rx_ring[RX_RING_SIZE]; + struct tx_desc tx_ring[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for later free(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + unsigned char *tx_buf[TX_RING_SIZE]; /* Tx bounce buffers */ + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + struct device *next_module; /* Link for devices of this type. */ + struct net_device_stats stats; + struct timer_list timer; /* Media monitoring timer. */ + unsigned char pci_bus, pci_devfn; + /* Frequently used values: keep some adjacent for cache effect. */ + int chip_id; + long in_interrupt; /* Word-long for SMP locks. */ + struct rx_desc *rx_head_desc; + unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ + unsigned int cur_tx, dirty_tx; + unsigned int rx_buf_sz; /* Based on MTU+slack. */ + u16 chip_cmd; /* Current setting for ChipCmd */ + unsigned int tx_full:1; /* The Tx queue is full. */ + /* These values are keep track of the transceiver/media in use. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int duplex_lock:1; + unsigned int medialock:1; /* Do not sense media. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + u8 tx_thresh, rx_thresh; + /* MII transceiver section. */ + int mii_cnt; /* MII device addresses. */ + u16 advertising; /* NWay media advertisement */ + unsigned char phys[2]; /* MII device addresses. */ +}; + +static int mdio_read(struct device *dev, int phy_id, int location); +static void mdio_write(struct device *dev, int phy_id, int location, int value); +static int netdev_open(struct device *dev); +static void check_duplex(struct device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct device *dev); +static void init_ring(struct device *dev); +static int start_tx(struct sk_buff *skb, struct device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static int netdev_rx(struct device *dev); +static void netdev_error(struct device *dev, int intr_status); +static void set_rx_mode(struct device *dev); +static struct net_device_stats *get_stats(struct device *dev); +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd); +static int netdev_close(struct device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct device *root_net_dev = NULL; + +/* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well when dynamically adding drivers. So instead we detect just the + cards we know about in slot order. */ + +static int pci_etherdev_probe(struct device *dev, struct pci_id_info pci_tbl[]) +{ + int cards_found = 0; + int pci_index = 0; + unsigned char pci_bus, pci_device_fn; + + if ( ! pcibios_present()) + return -ENODEV; + + for (;pci_index < 0xff; pci_index++) { + u16 vendor, device, pci_command, new_command; + int chip_idx, irq; + long pciaddr; + long ioaddr; + + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, + &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id + && (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) + break; + if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ + continue; + + { +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); +#ifdef VIA_USE_IO + pciaddr = pdev->base_address[0]; +#else + pciaddr = pdev->base_address[1]; +#endif + irq = pdev->irq; +#else + u32 pci_memaddr; + u8 pci_irq_line; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); +#ifdef VIA_USE_IO + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_memaddr); + pciaddr = pci_memaddr; +#else + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_memaddr); + pciaddr = pci_memaddr; +#endif + irq = pci_irq_line; +#endif + } + + if (debug > 2) + printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n", + pci_tbl[chip_idx].name, pciaddr, irq); + + if (pci_tbl[chip_idx].flags & PCI_USES_IO) { + if (check_region(pciaddr, pci_tbl[chip_idx].io_size)) + continue; + ioaddr = pciaddr & ~3; + } else if ((ioaddr = (long)ioremap(pciaddr & ~0xf, + pci_tbl[chip_idx].io_size)) == 0) { + printk(KERN_INFO "Failed to map PCI address %#lx.\n", + pciaddr); + continue; + } + + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | (pci_tbl[chip_idx].flags & 7); + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr, + irq, chip_idx, cards_found); + + if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { + u8 pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < min_pci_latency) { + printk(KERN_INFO " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to %d clocks.\n", + pci_latency, min_pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, min_pci_latency); + } + } + dev = 0; + cards_found++; + } + + return cards_found ? 0 : -ENODEV; +} + +#ifndef MODULE +int via_rhine_probe(struct device *dev) +{ + return pci_etherdev_probe(dev, pci_tbl); +} +#endif + +static struct device *via_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, int irq, + int chip_id, int card_idx) +{ + static int did_version = 0; /* Already printed version info */ + struct netdev_private *np; + int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + + if (debug > 0 && did_version++ == 0) + printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); + + dev = init_etherdev(dev, 0); + + printk(KERN_INFO "%s: %s at 0x%lx, ", + dev->name, pci_tbl[chip_id].name, ioaddr); + + /* Ideally we would be read the EEPROM but access may be locked. */ + for (i = 0; i <6; i++) + dev->dev_addr[i] = readb(ioaddr + StationAddr + i); + for (i = 0; i < 5; i++) + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +#ifdef VIA_USE_IO + request_region(ioaddr, pci_tbl[chip_id].io_size, dev->name); +#endif + + /* Reset the chip to erase previous misconfiguration. */ + writew(CmdReset, ioaddr + ChipCmd); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* Make certain the descriptor lists are cache-aligned. */ + np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 31) & ~31); + memset(np, 0, sizeof(*np)); + dev->priv = np; + + np->next_module = root_net_dev; + root_net_dev = dev; + + np->pci_bus = pci_bus; + np->pci_devfn = pci_devfn; + np->chip_id = chip_id; + + if (dev->mem_start) + option = dev->mem_start; + + /* The lower four bits are the media type. */ + if (option > 0) { + if (option & 0x200) + np->full_duplex = 1; + np->default_port = option & 15; + if (np->default_port) + np->medialock = 1; + } + if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) + np->full_duplex = 1; + + if (np->full_duplex) + np->duplex_lock = 1; + + /* The chip-specific entries in the device structure. */ + dev->open = &netdev_open; + dev->hard_start_xmit = &start_tx; + dev->stop = &netdev_close; + dev->get_stats = &get_stats; + dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &mii_ioctl; + + if (cap_tbl[np->chip_id].flags & CanHaveMII) { + int phy, phy_idx = 0; + np->phys[0] = 1; /* Standard for this chip. */ + for (phy = 1; phy < 32 && phy_idx < 4; phy++) { + int mii_status = mdio_read(dev, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + np->phys[phy_idx++] = phy; + np->advertising = mdio_read(dev, phy, 4); + printk(KERN_INFO "%s: MII PHY found at address %d, status " + "0x%4.4x advertising %4.4x Link %4.4x.\n", + dev->name, phy, mii_status, np->advertising, + mdio_read(dev, phy, 5)); + } + } + np->mii_cnt = phy_idx; + } + + return dev; +} + + +/* Read and write over the MII Management Data I/O (MDIO) interface. */ + +static int mdio_read(struct device *dev, int phy_id, int regnum) +{ + long ioaddr = dev->base_addr; + int boguscnt = 1024; + + /* Wait for a previous command to complete. */ + while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) + ; + writeb(0x00, ioaddr + MIICmd); + writeb(phy_id, ioaddr + MIIPhyAddr); + writeb(regnum, ioaddr + MIIRegAddr); + writeb(0x40, ioaddr + MIICmd); /* Trigger read */ + boguscnt = 1024; + while ((readb(ioaddr + MIICmd) & 0x40) && --boguscnt > 0) + ; + return readw(ioaddr + MIIData); +} + +static void mdio_write(struct device *dev, int phy_id, int regnum, int value) +{ + long ioaddr = dev->base_addr; + int boguscnt = 1024; + + /* Wait for a previous command to complete. */ + while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) + ; + writeb(0x00, ioaddr + MIICmd); + writeb(phy_id, ioaddr + MIIPhyAddr); + writeb(regnum, ioaddr + MIIRegAddr); + writew(value, ioaddr + MIIData); + writeb(0x20, ioaddr + MIICmd); /* Trigger write. */ + return; +} + + +static int netdev_open(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + /* Reset the chip. */ + writew(CmdReset, ioaddr + ChipCmd); + + if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + + if (debug > 1) + printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", + dev->name, dev->irq); + + MOD_INC_USE_COUNT; + + init_ring(dev); + + writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); + writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); + + for (i = 0; i < 6; i++) + writeb(dev->dev_addr[i], ioaddr + StationAddr + i); + + /* Initialize other registers. */ + writew(0x0006, ioaddr + PCIConfig); /* Tune configuration??? */ + /* Configure the FIFO thresholds. */ + writeb(0x20, ioaddr + TxConfig); /* Initial threshold 32 bytes */ + np->tx_thresh = 0x20; + np->rx_thresh = 0x60; /* Written in set_rx_mode(). */ + + if (dev->if_port == 0) + dev->if_port = np->default_port; + + dev->tbusy = 0; + dev->interrupt = 0; + np->in_interrupt = 0; + + set_rx_mode(dev); + + dev->start = 1; + + /* Enable interrupts by setting the interrupt mask. */ + writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped| + IntrTxDone | IntrTxAbort | IntrTxUnderrun | + IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange, + ioaddr + IntrEnable); + + np->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll; + writew(np->chip_cmd, ioaddr + ChipCmd); + + check_duplex(dev); + + if (debug > 2) + printk(KERN_DEBUG "%s: Done netdev_open(), status %4.4x " + "MII status: %4.4x.\n", + dev->name, readw(ioaddr + ChipCmd), + mdio_read(dev, np->phys[0], 1)); + + /* Set the timer to check for link beat. */ + init_timer(&np->timer); + np->timer.expires = RUN_AT(1); + np->timer.data = (unsigned long)dev; + np->timer.function = &netdev_timer; /* timer handler */ + add_timer(&np->timer); + + return 0; +} + +static void check_duplex(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + int mii_reg5 = mdio_read(dev, np->phys[0], 5); + int duplex; + + if (np->duplex_lock || mii_reg5 == 0xffff) + return; + duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040; + if (np->full_duplex != duplex) { + np->full_duplex = duplex; + if (debug) + printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" + " partner capability of %4.4x.\n", dev->name, + duplex ? "full" : "half", np->phys[0], mii_reg5); + if (duplex) + np->chip_cmd |= CmdFDuplex; + else + np->chip_cmd &= ~CmdFDuplex; + writew(np->chip_cmd, ioaddr + ChipCmd); + } +} + +static void netdev_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 10*HZ; + + if (debug > 3) { + printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n", + dev->name, readw(ioaddr + IntrStatus)); + } + check_duplex(dev); + + np->timer.expires = RUN_AT(next_tick); + add_timer(&np->timer); +} + +static void tx_timeout(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + + printk(KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status " + "%4.4x, resetting...\n", + dev->name, readw(ioaddr + IntrStatus), + mdio_read(dev, np->phys[0], 1)); + + /* Perhaps we should reinitialize the hardware here. */ + dev->if_port = 0; + /* Stop and restart the chip's Tx processes . */ + + /* Trigger an immediate transmit demand. */ + + dev->trans_start = jiffies; + np->stats.tx_errors++; + return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + int i; + + np->tx_full = 0; + np->cur_rx = np->cur_tx = 0; + np->dirty_rx = np->dirty_tx = 0; + + np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + np->rx_head_desc = &np->rx_ring[0]; + + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].rx_status = 0; + np->rx_ring[i].rx_length = 0; + np->rx_ring[i].desc_length = np->rx_buf_sz; + np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]); + np->rx_skbuff[i] = 0; + } + /* Mark the last entry as wrapping the ring. */ + np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]); + + /* Fill in the Rx buffers. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_ring[i].addr = virt_to_bus(skb->tail); + np->rx_ring[i].rx_status = 0; + np->rx_ring[i].rx_length = DescOwn; + } + np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + for (i = 0; i < TX_RING_SIZE; i++) { + np->tx_skbuff[i] = 0; + np->tx_ring[i].tx_own = 0; + np->tx_ring[i].desc_length = 0x00e08000; + np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]); + np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL); + } + np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]); + + return; +} + +static int start_tx(struct sk_buff *skb, struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + unsigned entry; + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + tx_timeout(dev); + return 1; + } + + /* Caution: the write order is important here, set the field + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = np->cur_tx % TX_RING_SIZE; + + np->tx_skbuff[entry] = skb; + + if ((long)skb->data & 3) { /* Must use alignment buffer. */ + if (np->tx_buf[entry] == NULL && + (np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL) + return 1; + memcpy(np->tx_buf[entry], skb->data, skb->len); + np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]); + } else + np->tx_ring[entry].addr = virt_to_bus(skb->data); + + np->tx_ring[entry].desc_length = 0x00E08000 | + (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN); + np->tx_ring[entry].tx_own = DescOwn; + + np->cur_tx++; + + /* Non-x86 Todo: explicitly flush cache lines here. */ + + /* Wake the potentially-idle transmit channel. */ + writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + + if (np->cur_tx - np->dirty_tx < TX_RING_SIZE - 1) + clear_bit(0, (void*)&dev->tbusy); /* Typical path */ + else + np->tx_full = 1; + dev->trans_start = jiffies; + + if (debug > 4) { + printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", + dev->name, np->cur_tx, entry); + } + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ + struct device *dev = (struct device *)dev_instance; + struct netdev_private *np; + long ioaddr, boguscnt = max_interrupt_work; + + ioaddr = dev->base_addr; + np = (struct netdev_private *)dev->priv; +#if defined(__i386__) + /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", + dev->name); + dev->interrupt = 0; /* Avoid halting machine. */ + return; + } +#else + if (dev->interrupt) { + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; +#endif + + do { + u32 intr_status = readw(ioaddr + IntrStatus); + + /* Acknowledge all of the current interrupt sources ASAP. */ + writew(intr_status & 0xffff, ioaddr + IntrStatus); + + if (debug > 4) + printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", + dev->name, intr_status); + + if (intr_status == 0) + break; + + if (intr_status & (IntrRxDone | IntrRxErr | IntrRxDropped | + IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf)) + netdev_rx(dev); + + for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { + int entry = np->dirty_tx % TX_RING_SIZE; + int txstatus; + if (np->tx_ring[entry].tx_own) + break; + txstatus = np->tx_ring[entry].tx_status; + if (debug > 6) + printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n", + entry, txstatus); + if (txstatus & 0x8000) { + if (debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n", + dev->name, txstatus); + np->stats.tx_errors++; + if (txstatus & 0x0400) np->stats.tx_carrier_errors++; + if (txstatus & 0x0200) np->stats.tx_window_errors++; + if (txstatus & 0x0100) np->stats.tx_aborted_errors++; + if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++; + if (txstatus & 0x0002) np->stats.tx_fifo_errors++; +#ifdef ETHER_STATS + if (txstatus & 0x0100) np->stats.collisions16++; +#endif + /* Transmitter restarted in 'abnormal' handler. */ + } else { +#ifdef ETHER_STATS + if (txstatus & 0x0001) np->stats.tx_deferred++; +#endif + np->stats.collisions += (txstatus >> 3) & 15; +#if defined(NETSTATS_VER2) + np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff; +#endif + np->stats.tx_packets++; + } + /* Free the original skb. */ + dev_free_skb(np->tx_skbuff[entry]); + np->tx_skbuff[entry] = 0; + } + if (np->tx_full && dev->tbusy + && np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) { + /* The ring is no longer full, clear tbusy. */ + np->tx_full = 0; + clear_bit(0, (void*)&dev->tbusy); + mark_bh(NET_BH); + } + + /* Abnormal error summary/uncommon events handlers. */ + if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange | + IntrStatsMax | IntrTxAbort | IntrTxUnderrun)) + netdev_error(dev, intr_status); + + if (--boguscnt < 0) { + printk(KERN_WARNING "%s: Too much work at interrupt, " + "status=0x%4.4x.\n", + dev->name, intr_status); + break; + } + } while (1); + + if (debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", + dev->name, readw(ioaddr + IntrStatus)); + +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else + dev->interrupt = 0; +#endif + return; +} + +/* This routine is logically part of the interrupt handler, but isolated + for clarity and better register allocation. */ +static int netdev_rx(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + int entry = np->cur_rx % RX_RING_SIZE; + int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; + + if (debug > 4) { + printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", + entry, np->rx_head_desc->rx_length); + } + + /* If EOP is set on the next entry, it's a new packet. Send it up. */ + while ( ! (np->rx_head_desc->rx_length & DescOwn)) { + struct rx_desc *desc = np->rx_head_desc; + int data_size = desc->rx_length; + u16 desc_status = desc->rx_status; + + if (debug > 4) + printk(KERN_DEBUG " netdev_rx() status is %4.4x.\n", + desc_status); + if (--boguscnt < 0) + break; + if ( (desc_status & (RxWholePkt | RxErr)) != RxWholePkt) { + if ((desc_status & RxWholePkt) != RxWholePkt) { + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " + "multiple buffers, entry %#x length %d status %4.4x!\n", + dev->name, np->cur_rx, data_size, desc_status); + printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", + dev->name, np->rx_head_desc, + &np->rx_ring[np->cur_rx % RX_RING_SIZE]); + np->stats.rx_length_errors++; + } else if (desc_status & RxErr) { + /* There was a error. */ + if (debug > 2) + printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n", + desc_status); + np->stats.rx_errors++; + if (desc_status & 0x0030) np->stats.rx_length_errors++; + if (desc_status & 0x0048) np->stats.rx_fifo_errors++; + if (desc_status & 0x0004) np->stats.rx_frame_errors++; + if (desc_status & 0x0002) np->stats.rx_crc_errors++; + } + } else { + struct sk_buff *skb; + /* Length should omit the CRC */ + u16 pkt_len = data_size - 4; + + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if ! defined(__alpha__) || USE_IP_COPYSUM /* Avoid misaligned on Alpha */ + eth_copy_and_sum(skb, bus_to_virt(desc->addr), + pkt_len, 0); + skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb,pkt_len), bus_to_virt(desc->addr), pkt_len); +#endif + } else { + skb_put(skb = np->rx_skbuff[entry], pkt_len); + np->rx_skbuff[entry] = NULL; + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + np->stats.rx_packets++; + } + entry = (++np->cur_rx) % RX_RING_SIZE; + np->rx_head_desc = &np->rx_ring[entry]; + } + + /* Refill the Rx ring buffers. */ + for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { + struct sk_buff *skb; + entry = np->dirty_rx % RX_RING_SIZE; + if (np->rx_skbuff[entry] == NULL) { + skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[entry] = skb; + if (skb == NULL) + break; /* Better luck next round. */ + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_ring[entry].addr = virt_to_bus(skb->tail); + } + np->rx_ring[entry].rx_status = 0; + np->rx_ring[entry].rx_length = DescOwn; + } + + /* Pre-emptively restart Rx engine. */ + writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + return 0; +} + +static void netdev_error(struct device *dev, int intr_status) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + + if (intr_status & (IntrMIIChange | IntrLinkChange)) { + if (readb(ioaddr + MIIStatus) & 0x02) + /* Link failed, restart autonegotiation. */ + mdio_write(dev, np->phys[0], 0, 0x3300); + else + check_duplex(dev); + if (debug) + printk(KERN_ERR "%s: MII status changed: Autonegotiation " + "advertising %4.4x partner %4.4x.\n", dev->name, + mdio_read(dev, np->phys[0], 4), + mdio_read(dev, np->phys[0], 5)); + } + if (intr_status & IntrStatsMax) { + np->stats.rx_crc_errors += readw(ioaddr + RxCRCErrs); + np->stats.rx_missed_errors += readw(ioaddr + RxMissed); + writel(0, RxMissed); + } + if (intr_status & IntrTxAbort) { + /* Stats counted in Tx-done handler, just restart Tx. */ + writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + } + if (intr_status & IntrTxUnderrun) { + if (np->tx_thresh < 0xE0) + writeb(np->tx_thresh += 0x20, ioaddr + TxConfig); + if (debug > 1) + printk(KERN_INFO "%s: Transmitter underrun, increasing Tx " + "threshold setting to %2.2x.\n", dev->name, np->tx_thresh); + } + if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug) { + printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", + dev->name, intr_status); + /* Recovery for other fault sources not known. */ + writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + } +} + +static struct enet_statistics *get_stats(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + + /* Nominally we should lock this segment of code for SMP, although + the vulnerability window is very small and statistics are + non-critical. */ + np->stats.rx_crc_errors += readw(ioaddr + RxCRCErrs); + np->stats.rx_missed_errors += readw(ioaddr + RxMissed); + writel(0, RxMissed); + + return &np->stats; +} + +/* The big-endian AUTODIN II ethernet CRC calculation. + N.B. Do not use for bulk data, use a table-based routine instead. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ + int crc = -1; + + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) { + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + } + return crc; +} + +static void set_rx_mode(struct device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + u8 rx_mode; /* Note: 0x02=accept runt, 0x01=accept errs */ + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + /* Unconditionally log net taps. */ + printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); + rx_mode = 0x1C; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to match, or accept all multicasts. */ + rx_mode = 0x0C; + } else { + struct dev_mc_list *mclist; + int i; + memset(mc_filter, 0, sizeof(mc_filter)); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, + mc_filter); + } + writel(mc_filter[0], ioaddr + MulticastFilter0); + writel(mc_filter[1], ioaddr + MulticastFilter1); + rx_mode = 0x0C; + } + writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig); +} + +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + u16 *data = (u16 *)&rq->ifr_data; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f; + /* Fall Through */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int netdev_close(struct device *dev) +{ + long ioaddr = dev->base_addr; + struct netdev_private *np = (struct netdev_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, readw(ioaddr + ChipCmd)); + + /* Disable interrupts by clearing the interrupt mask. */ + writew(0x0000, ioaddr + IntrEnable); + + /* Stop the chip's Tx and Rx processes. */ + writew(CmdStop, ioaddr + ChipCmd); + + del_timer(&np->timer); + + free_irq(dev->irq, dev); + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].rx_length = 0; + np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ + if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 + np->rx_skbuff[i]->free = 1; +#endif + dev_free_skb(np->rx_skbuff[i]); + } + np->rx_skbuff[i] = 0; + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (np->tx_skbuff[i]) + dev_free_skb(np->tx_skbuff[i]); + np->tx_skbuff[i] = 0; + } + + MOD_DEC_USE_COUNT; + + return 0; +} + + +#ifdef MODULE +int init_module(void) +{ + if (debug) /* Emit version even if no cards detected. */ + printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); +#ifdef CARDBUS + register_driver(ðerdev_ops); + return 0; +#else + return pci_etherdev_probe(NULL, pci_tbl); +#endif +} + +void cleanup_module(void) +{ + +#ifdef CARDBUS + unregister_driver(ðerdev_ops); +#endif + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_net_dev) { + struct netdev_private *np = + (struct netdev_private *)(root_net_dev->priv); + unregister_netdev(root_net_dev); +#ifdef VIA_USE_IO + release_region(root_net_dev->base_addr, pci_tbl[np->chip_id].io_size); +#else + iounmap((char *)(root_net_dev->base_addr)); +#endif + kfree(root_net_dev); + root_net_dev = np->next_module; +#if 0 + kfree(np); /* Assumption: no struct realignment. */ +#endif + } +} + +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c via-rhine.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c via-rhine.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.0.35/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c --- v2.0.35/linux/drivers/net/wavelan.c Sun Nov 15 10:49:41 1998 +++ linux/drivers/net/wavelan.c Sun Nov 15 10:33:04 1998 @@ -2967,8 +2967,8 @@ /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) * (does it work for everybody? -- especially old cards?) */ /* Note: WFREQSEL verifies that it is able to read a sensible - * frequency from from EEPROM (address 0x00) and that - * MMR_FEE_STATUS_ID is 0xA (Xilinx version) or 0xB (Ariadne version). + * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID + * is 0xA (Xilinx version) or 0xB (Ariadne version). * My test is more crude but does work. */ if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) diff -u --recursive --new-file v2.0.35/linux/drivers/net/wic.c linux/drivers/net/wic.c --- v2.0.35/linux/drivers/net/wic.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/wic.c Wed Dec 31 16:00:00 1969 @@ -1,1459 +0,0 @@ -/* $Id: wic.c,v 1.3.2.1 1997/03/09 02:14:40 davem Exp $ */ -/* WIC: A parallel port "network" driver for Linux. */ -/* based on the plip network driver */ -/* Modified for Linux 1.3.x by Alan Cox */ - -char *version = "NET3 WIC version 0.9 hayes@netplumbing.com"; - -/* - Sources: - Ideas and protocols came from Russ Nelson's - "parallel.asm" parallel port packet driver and from the plip.c - parallel networking linux driver from the 1.2.13 Linux - distribution. - - The packet is encapsulated as if it were ethernet. - -*/ - -#ifdef MODULE -#include -#include -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif - -#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 NET_DEBUG 1 -/* Use 0 for production, 1 for verification, >2 for debug */ -#ifndef NET_DEBUG -#define NET_DEBUG 1 -#endif -unsigned int net_debug = NET_DEBUG; - -/* Connection time out = WIC_TRIGGER_WAIT * WIC_DELAY_UNIT usec */ -#define WIC_TRIGGER_WAIT 500 - -/* Nibble time out = WIC_NIBBLE_WAIT * WIC_DELAY_UNIT usec */ -#define WIC_NIBBLE_WAIT 3000 - -#define PAR_DATA(dev) ((dev)->base_addr+0) -#define PAR_STATUS(dev) ((dev)->base_addr+1) -#define PAR_CONTROL(dev) ((dev)->base_addr+2) - -/* Bottom halfs */ -void wic_kick_bh(struct device *dev); -void wic_bh(struct device *dev); - -/* Interrupt handler */ -void wic_interrupt(int irq, void *dev_ptr, struct pt_regs *regs); - -/* Functions for DEV methods */ -int wic_rebuild_header(void *buff, struct device *dev, - unsigned long raddr, struct sk_buff *skb); -int wic_tx_packet(struct sk_buff *skb, struct device *dev); -int wic_open(struct device *dev); -int wic_close(struct device *dev); -struct enet_statistics *wic_get_stats(struct device *dev); -int wic_config(struct device *dev, struct ifmap *map); -int wic_ioctl(struct device *dev, struct ifreq *ifr, int cmd); -int send_cmd(struct device *dev, unsigned char *cmd, char len); -int recv_cmd_resp(struct device *dev, unsigned char *cmd); -int send_byte(struct device *dev, unsigned char c); -int get_byte(struct device *dev, unsigned char *c); -int ack_resp(struct device *dev); -int check_bfr(struct device *dev); -void wic_reset(struct device *dev); -void wic_set_multicast_list(struct device *dev); - -#define LOOPCNT 30000 -unsigned char tog = 3; -unsigned char save = 0; - -enum wic_connection_state { - WIC_CN_NONE=0, - WIC_CN_RECEIVE, - WIC_CN_SEND, - WIC_CN_CLOSING, - WIC_CN_ERROR -}; - -enum wic_packet_state { - WIC_PK_DONE=0, - WIC_PK_TRIGGER, - WIC_PK_LENGTH_LSB, - WIC_PK_LENGTH_MSB, - WIC_PK_DATA, - WIC_PK_CHECKSUM -}; - -enum wic_nibble_state { - WIC_NB_BEGIN, - WIC_NB_1, - WIC_NB_2, -}; - -struct wic_local { - enum wic_packet_state state; - enum wic_nibble_state nibble; - union { - struct { -#if defined(__LITTLE_ENDIAN) - unsigned char lsb; - unsigned char msb; -#elif defined(__BIG_ENDIAN) - unsigned char msb; - unsigned char lsb; -#else -#error "Please fix the endianness defines in " -#endif - } b; - unsigned short h; - } length; - unsigned short byte; - unsigned char checksum; - unsigned char data; - struct sk_buff *skb; -}; - -struct net_local { - struct enet_statistics enet_stats; - struct tq_struct immediate; - struct tq_struct deferred; - struct wic_local snd_data; - struct wic_local rcv_data; - unsigned long trigger; - unsigned long nibble; - enum wic_connection_state connection; - unsigned short timeout_count; - char is_deferred; - int (*orig_rebuild_header)(void *eth, struct device *dev, - unsigned long raddr, struct sk_buff *skb); -}; - -/* Entry point of WIC driver. - Probe the hardware, and register/initialize the driver. */ -int -wic_init(struct device *dev) -{ - struct net_local *nl; - struct wicconf wc; - int i; - - /* Check region before the probe */ - if (check_region(PAR_DATA(dev), 3) < 0) - return -ENODEV; - - /* Check that there is something at base_addr. */ - outb(0, PAR_DATA(dev)); - udelay(1000); - if (inb(PAR_DATA(dev)) != 0) - return -ENODEV; - - printk("%s\n",version); - printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr); - if (dev->irq) { - printk("using assigned IRQ %d.\n", dev->irq); - } else { - int irq = 0; -#ifdef MODULE - /* dev->irq==0 means autoprobe, but we don't try to do so - with module. We can change it by ifconfig */ -#else - unsigned int irqs = probe_irq_on(); - - outb(0x00, PAR_CONTROL(dev)); - udelay(1000); - udelay(1000); - irq = probe_irq_off(irqs); -#endif - if (irq > 0) { - dev->irq = irq; - printk("using probed IRQ %d.\n", dev->irq); - } else - printk("failed to detect IRQ(%d) --" - " Please set IRQ by ifconfig.\n", irq); - } - - request_region(PAR_DATA(dev), 3, dev->name); - - /* Fill in the generic fields of the device structure. */ - ether_setup(dev); - - /* Then, override parts of it */ - dev->hard_start_xmit = wic_tx_packet; - dev->open = wic_open; - dev->stop = wic_close; - dev->get_stats = wic_get_stats; - dev->set_config = wic_config; - dev->do_ioctl = wic_ioctl; - dev->mtu = 1514; - dev->set_multicast_list = wic_set_multicast_list; - dev->flags = IFF_BROADCAST | IFF_RUNNING | IFF_NOTRAILERS; - - /* get the MAC address from the controller */ - wc.len = 1; - wc.pcmd = WIC_GETNET; - check_bfr(dev); - send_cmd(dev, (unsigned char *)&wc, 1); - wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data); - while ((wc.len == 1) && (wc.data[0] == 0x7)) /* controller int */ - wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data); - - printk("%s:MAC address: ",dev->name); - for (i=0; i < ETH_ALEN ; i++) { - dev->dev_addr[i] = wc.data[i]; - printk("%2x ",dev->dev_addr[i]); - } - printk("\n"); - - /* Set the private structure */ - dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL); - if (dev->priv == NULL) - return EAGAIN; - memset(dev->priv, 0, sizeof(struct net_local)); - nl = (struct net_local *) dev->priv; - - nl->orig_rebuild_header = dev->rebuild_header; - dev->rebuild_header = wic_rebuild_header; - - /* Initialize constants */ - nl->trigger = WIC_TRIGGER_WAIT; - nl->nibble = WIC_NIBBLE_WAIT; - - /* Initialize task queue structures */ - nl->immediate.next = NULL; - nl->immediate.sync = 0; - nl->immediate.routine = (void *)(void *)wic_bh; - nl->immediate.data = dev; - - nl->deferred.next = NULL; - nl->deferred.sync = 0; - nl->deferred.routine = (void *)(void *)wic_kick_bh; - nl->deferred.data = dev; - - return 0; -} - -/* Bottom half handler for the delayed request. - This routine is kicked by do_timer(). - Request `wic_bh' to be invoked. */ -void -wic_kick_bh(struct device *dev) -{ - struct net_local *nl = (struct net_local *)dev->priv; - - if (nl->is_deferred) { - queue_task(&nl->immediate, &tq_immediate); - mark_bh(IMMEDIATE_BH); - } -} - -/* Forward declarations of internal routines */ -int wic_none(struct device *, struct net_local *, - struct wic_local *, struct wic_local *); -int wic_receive_packet(struct device *, struct net_local *, - struct wic_local *, struct wic_local *); -int wic_send_packet(struct device *, struct net_local *, - struct wic_local *, struct wic_local *); -int wic_connection_close(struct device *, struct net_local *, - struct wic_local *, struct wic_local *); -int wic_error(struct device *, struct net_local *, - struct wic_local *, struct wic_local *); -int wic_bh_timeout_error(struct device *dev, struct net_local *nl, - struct wic_local *snd, - struct wic_local *rcv, - int error); - -#define OK 0 -#define TIMEOUT 1 -#define ERROR 2 - -typedef int (*wic_func)(struct device *dev, struct net_local *nl, - struct wic_local *snd, struct wic_local *rcv); - -wic_func connection_state_table[] = -{ - wic_none, - wic_receive_packet, - wic_send_packet, - wic_connection_close, - wic_error -}; - -void -wic_set_multicast_list(struct device *dev) -{ - struct wicconf wc; - struct wic_net *wn; - - disable_irq(dev->irq); - save &= 0xef; /* disable */ - outb(save, PAR_CONTROL(dev)); - - wc.len = 1; - wc.pcmd = WIC_GETNET; - check_bfr(dev); - tog = 3; - send_cmd(dev, (unsigned char *)&wc, 1); - wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data); - while ((wc.len == 1) && (wc.data[0] == 0x7)) /* controller int */ - wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data); - wn = (struct wic_net *)&wc.data; - if(dev->flags&IFF_PROMISC) - { - /* promiscuous mode */ - wn->mode |= (NET_MODE_ME | NET_MODE_BCAST | - NET_MODE_MCAST | NET_MODE_PROM); - printk("%s: Setting promiscuous mode\n", dev->name); - } - else if((dev->flags&IFF_ALLMULTI) || dev->mc_count) - { - wn->mode &= ~NET_MODE_PROM; - wn->mode |= (NET_MODE_MCAST | NET_MODE_ME | NET_MODE_BCAST); - } - else - { - wn->mode &= ~(NET_MODE_PROM | NET_MODE_MCAST); - wn->mode |= (NET_MODE_ME | NET_MODE_BCAST); - } - wc.len = 23; - wc.pcmd = WIC_SETNET; - check_bfr(dev); - tog = 3; - send_cmd(dev, (unsigned char *)&wc, wc.len); - - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - return; -} - -/* Bottom half handler of WIC. */ -void -wic_bh(struct device *dev) -{ - struct net_local *nl = (struct net_local *)dev->priv; - struct wic_local *snd = &nl->snd_data; - struct wic_local *rcv = &nl->rcv_data; - wic_func f; - int r; - - nl->is_deferred = 0; - f = connection_state_table[nl->connection]; - if ((r = (*f)(dev, nl, snd, rcv)) != OK - && (r = wic_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) { - nl->is_deferred = 1; - queue_task(&nl->deferred, &tq_timer); - } -} - -int -wic_bh_timeout_error(struct device *dev, struct net_local *nl, - struct wic_local *snd, struct wic_local *rcv, - int error) -{ - unsigned char c0; - unsigned long flags; - - save_flags(flags); - cli(); - if (nl->connection == WIC_CN_SEND) { - - if (error != ERROR) { /* Timeout */ - nl->timeout_count++; - if ((snd->state == WIC_PK_TRIGGER - && nl->timeout_count <= 10) - || nl->timeout_count <= 3) { - restore_flags(flags); - /* Try again later */ - return TIMEOUT; - } - c0 = inb(PAR_STATUS(dev)); - printk("%s: transmit timeout(%d,%02x)\n", - dev->name, snd->state, c0); - } - nl->enet_stats.tx_errors++; - nl->enet_stats.tx_aborted_errors++; - } else if (nl->connection == WIC_CN_RECEIVE) { - if (rcv->state == WIC_PK_TRIGGER) { - /* Transmission was interrupted. */ - restore_flags(flags); - return OK; - } - if (error != ERROR) { /* Timeout */ - if (++nl->timeout_count <= 3) { - restore_flags(flags); - /* Try again later */ - return TIMEOUT; - } - c0 = inb(PAR_STATUS(dev)); - printk("%s: receive timeout(%d,%02x)\n", - dev->name, rcv->state, c0); - } - nl->enet_stats.rx_dropped++; - } - rcv->state = WIC_PK_DONE; - if (rcv->skb) { - rcv->skb->free = 1; - kfree_skb(rcv->skb, FREE_READ); - rcv->skb = NULL; - } - snd->state = WIC_PK_DONE; - if (snd->skb) { - snd->skb->free = 1; - dev_kfree_skb(snd->skb, FREE_WRITE); - snd->skb = NULL; - } -#if (0) - disable_irq(dev->irq); - save &= 0xef; /* disable */ - outb(save, PAR_CONTROL(dev)); - dev->tbusy = 1; - outb(0x00, PAR_DATA(dev)); -#endif /* (0) */ - nl->connection = WIC_CN_ERROR; - restore_flags(flags); - - return TIMEOUT; -} - -int -wic_none(struct device *dev, struct net_local *nl, - struct wic_local *snd, struct wic_local *rcv) -{ - return OK; -} - -/* WIC_RECEIVE --- receive a byte(two nibbles) - Returns OK on success, TIMEOUT on timeout */ -inline int -wic_receive(unsigned short nibble_timeout, unsigned short status_addr, - enum wic_nibble_state *ns_p, unsigned char *data_p) -{ -unsigned int cx; - - cx = LOOPCNT; - while ((inb(status_addr) & 0x08) != ((tog<<3) & 0x08)) { - if (--cx == 0) { - return TIMEOUT; - } - } - *data_p = inb(status_addr-1); - tog ^= 0x01; - outb(tog| save, status_addr+1); - return OK; -} - -/* WIC_RECEIVE_PACKET --- receive a packet */ -int -wic_receive_packet(struct device *dev, struct net_local *nl, - struct wic_local *snd, struct wic_local *rcv) -{ - unsigned short status_addr = PAR_STATUS(dev); - unsigned short nibble_timeout = nl->nibble; - unsigned char *lbuf; - unsigned char junk; - unsigned long flags; - - save_flags(flags); - cli(); - switch (rcv->state) { - case WIC_PK_TRIGGER: - disable_irq(dev->irq); - save &= 0xef; /* disable */ - outb(save, PAR_CONTROL(dev)); - - dev->interrupt = 0; - - tog &= 0xfe; - ack_resp(dev); - if (net_debug > 2) - printk("%s: receive start\n", dev->name); - rcv->state = WIC_PK_LENGTH_LSB; - rcv->nibble = WIC_NB_BEGIN; - - case WIC_PK_LENGTH_LSB: - if (net_debug > 2) - printk("%s: WIC_PK_LENGTH_LSB\n", dev->name); - if (snd->state != WIC_PK_DONE) { - if (wic_receive(nl->trigger, status_addr, - &rcv->nibble, &rcv->length.b.lsb)) { - /* collision, here dev->tbusy == 1 */ - rcv->state = WIC_PK_DONE; - nl->is_deferred = 1; - nl->connection = WIC_CN_SEND; - restore_flags(flags); - queue_task(&nl->deferred, &tq_timer); - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - return OK; - } - } else { - if (wic_receive(nibble_timeout, status_addr, - &rcv->nibble, &rcv->length.b.lsb)) { - restore_flags(flags); - return TIMEOUT; - } - } - rcv->state = WIC_PK_LENGTH_MSB; - - case WIC_PK_LENGTH_MSB: - if (net_debug > 2) - printk("%s: WIC_PK_LENGTH_MSB\n", dev->name); - if (wic_receive(nibble_timeout, status_addr, - &rcv->nibble, &rcv->length.b.msb)) { - restore_flags(flags); - return TIMEOUT; - } - if (rcv->length.h > dev->mtu || rcv->length.h < 8) { - printk("%s: bad packet size %d.\n", dev->name, rcv->length.h); - restore_flags(flags); - return ERROR; - } - /* Malloc up new buffer. */ - rcv->skb = dev_alloc_skb(rcv->length.h); - if (rcv->skb == NULL) { - printk("%s: Memory squeeze.\n", dev->name); - restore_flags(flags); - return ERROR; - } - skb_put(rcv->skb,rcv->length.h); - rcv->skb->dev = dev; - - rcv->state = WIC_PK_DATA; - rcv->byte = 0; - rcv->checksum = 0; - - /* sequence numbers */ - if (net_debug > 2) - printk("%s: WIC_PK_SEQ\n", dev->name); - if (wic_receive(nibble_timeout, status_addr, - &rcv->nibble, &junk)) { - restore_flags(flags); - return TIMEOUT; - } - if (wic_receive(nibble_timeout, status_addr, - &rcv->nibble, &junk)) { - restore_flags(flags); - return TIMEOUT; - } - - case WIC_PK_DATA: - if (net_debug > 2) - printk("%s: WIC_PK_DATA: length %i\n", dev->name, - rcv->length.h); - lbuf = rcv->skb->data; - do { - if (wic_receive(nibble_timeout, status_addr, - &rcv->nibble, &lbuf[rcv->byte])) { - restore_flags(flags); - return TIMEOUT; - } - } while (++rcv->byte < (rcv->length.h - 4)); - - /* receive pad byte */ - if (rcv->length.h & 0x01) - wic_receive(nibble_timeout, status_addr, - &rcv->nibble, &lbuf[rcv->byte]); - - do { - rcv->checksum += lbuf[--rcv->byte]; - } while (rcv->byte); - - rcv->state = WIC_PK_CHECKSUM; - - case WIC_PK_CHECKSUM: - if (net_debug > 2) - printk("%s: WIC_PK_CHECKSUM\n", dev->name); - if (wic_receive(nibble_timeout, status_addr, - &rcv->nibble, &junk)) { - restore_flags(flags); - return TIMEOUT; - } - outb(0, PAR_DATA(dev)); - rcv->state = WIC_PK_DONE; - - case WIC_PK_DONE: - if (net_debug > 2) - printk("%s: WIC_PK_DONE\n", dev->name); - /* Inform the upper layer for the arrival of a packet. */ - netif_rx(rcv->skb); - nl->enet_stats.rx_packets++; - rcv->skb = NULL; - if (net_debug > 2) - printk("%s: receive end\n", dev->name); - - /* Close the connection. */ - if (snd->state != WIC_PK_DONE) { - nl->connection = WIC_CN_SEND; - restore_flags(flags); - queue_task(&nl->immediate, &tq_immediate); - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - return OK; - } else { - nl->connection = WIC_CN_NONE; - restore_flags(flags); - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - return OK; - } - } - restore_flags(flags); - return OK; -} - -/* WIC_SEND --- send a byte (two nibbles) - Returns OK on success, TIMEOUT when timeout */ -inline int -wic_send(unsigned short nibble_timeout, unsigned short data_addr, - enum wic_nibble_state *ns_p, unsigned char data) -{ -unsigned int cx; - - cx = LOOPCNT; - while ((inb(data_addr+1) & 0x80) == ((tog<<7) & 0x80)) { - if (--cx == 0) { - return -TIMEOUT; - } - } - outb(data, data_addr); - outb(tog | save, data_addr+2); - tog ^= 0x01; - return OK; -} - -/* WIC_SEND_PACKET --- send a packet */ -int -wic_send_packet(struct device *dev, struct net_local *nl, - struct wic_local *snd, struct wic_local *rcv) -{ - unsigned short data_addr = PAR_DATA(dev); - unsigned short nibble_timeout = nl->nibble; - unsigned char *lbuf; - unsigned int cx; - unsigned int pad = 2; - unsigned long flags; - - if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) { - printk("%s: send skb lost\n", dev->name); - snd->state = WIC_PK_DONE; - snd->skb = NULL; - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - return ERROR; - } - - save_flags(flags); - cli(); - switch (snd->state) { - case WIC_PK_TRIGGER: - - if (nl->connection == WIC_CN_RECEIVE) { - /* interrupted */ - nl->enet_stats.collisions++; - restore_flags(flags); - if (net_debug > 1) - printk("%s: collision.\n", dev->name); - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - return OK; - } - - disable_irq(dev->irq); - save &= 0xef; /* disable */ - outb(save, PAR_CONTROL(dev)); - - /* interrupt controller */ - tog = 3; - outb(0x06 | save, PAR_CONTROL(dev)); - - cx = LOOPCNT; - while ((inb(PAR_STATUS(dev)) & 0xe8) != 0xc0) { - if (--cx == 0) { - restore_flags(flags); - return TIMEOUT; - } - if (cx == 10) - outb(0x02, PAR_CONTROL(dev)); - } - - if (net_debug > 2) - printk("%s: send start\n", dev->name); - snd->state = WIC_PK_LENGTH_LSB; - snd->nibble = WIC_NB_BEGIN; - nl->timeout_count = 0; - - case WIC_PK_LENGTH_LSB: - if (snd->length.h & 0x01) - pad = 3; - else - pad = 2; - snd->length.h += (4 + pad); /* len + seq + data + pad */ - if (net_debug > 2) - printk("%s: WIC_PK_LENGTH_LSB: length = %i\n", - dev->name, snd->length.h); - - if (wic_send(nibble_timeout, data_addr, - &snd->nibble, snd->length.b.lsb)) { - restore_flags(flags); - return TIMEOUT; - } - snd->state = WIC_PK_LENGTH_MSB; - - case WIC_PK_LENGTH_MSB: - if (net_debug > 2) - printk("%s: WIC_PK_LENGTH_MSB\n", dev->name); - if (wic_send(nibble_timeout, data_addr, - &snd->nibble, snd->length.b.msb)) { - restore_flags(flags); - return TIMEOUT; - } - snd->state = WIC_PK_DATA; - snd->byte = 0; - snd->checksum = 0; - - case WIC_PK_DATA: - /* adjust length back to data only */ - snd->length.h -= (4 + pad); /* len + seq + data + pad */ - /* send 2 byte sequence number */ - if (net_debug > 2) - printk("%s: WIC_SEQ\n", dev->name); - if (wic_send(nibble_timeout, data_addr, - &snd->nibble, 0)) { - restore_flags(flags); - return TIMEOUT; - } - if (wic_send(nibble_timeout, data_addr, - &snd->nibble, 0)) { - restore_flags(flags); - return TIMEOUT; - } - if (net_debug > 2) - printk("%s: WIC_PK_DATA\n", dev->name); - - do { - if (wic_send(nibble_timeout, data_addr, - &snd->nibble, lbuf[snd->byte])) { - restore_flags(flags); - return TIMEOUT; - } - } - while (++snd->byte < snd->length.h); - - do - snd->checksum += lbuf[--snd->byte]; - while (snd->byte); - - snd->state = WIC_PK_CHECKSUM; - - case WIC_PK_CHECKSUM: - /* send pad bytes */ - if (net_debug > 2) - printk("%s: WIC_PK_PAD: %i bytes\n", - dev->name, pad); - while(pad--) - if (wic_send(nibble_timeout, data_addr, - &snd->nibble, 0)) { - restore_flags(flags); - return TIMEOUT; - } - dev_kfree_skb(snd->skb, FREE_WRITE); - nl->enet_stats.tx_packets++; - snd->state = WIC_PK_DONE; - - case WIC_PK_DONE: - if (net_debug > 2) - printk("%s: WIC_PK_DONE\n", dev->name); - /* Close the connection */ - outb (0x00, PAR_DATA(dev)); - outb(save, PAR_CONTROL(dev)); - - snd->skb = NULL; - if (net_debug > 2) - printk("%s: send end\n", dev->name); - nl->connection = WIC_CN_CLOSING; - nl->is_deferred = 1; - restore_flags(flags); - queue_task(&nl->deferred, &tq_timer); - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - return OK; - } - restore_flags(flags); - return OK; -} - -int -wic_connection_close(struct device *dev, struct net_local *nl, - struct wic_local *snd, struct wic_local *rcv) -{ -unsigned long flags; - - save_flags(flags); - cli(); - if (nl->connection == WIC_CN_CLOSING) { - nl->connection = WIC_CN_NONE; - dev->tbusy = 0; - mark_bh(NET_BH); - } - restore_flags(flags); - return OK; -} - -/* WIC_ERROR --- wait till other end settled */ -int -wic_error(struct device *dev, struct net_local *nl, - struct wic_local *snd, struct wic_local *rcv) -{ - unsigned char status; - - status = inb(PAR_STATUS(dev)); - if ((status & 0xf8) == 0x80) { - if (net_debug > 2) - printk("%s: reset interface.\n", dev->name); - nl->connection = WIC_CN_NONE; - dev->tbusy = 0; - dev->interrupt = 0; - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - mark_bh(NET_BH); - } else { - nl->is_deferred = 1; - queue_task(&nl->deferred, &tq_timer); - } - - return OK; -} - -/* Handle the parallel port interrupts. */ -void -wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs) -{ - struct device *dev = (struct device *) irq2dev_map[irq]; - struct net_local *nl = (struct net_local *)dev->priv; - struct wic_local *rcv = &nl->rcv_data; - unsigned long flags; - - if (dev == NULL) { - printk ("wic_interrupt: irq %d for unknown device.\n", irq); - return; - } - - if (dev->interrupt) { - return; - } - - if (check_bfr(dev) < 0) { - return; - } - - dev->interrupt = 1; - if (net_debug > 3) - printk("%s: interrupt.\n", dev->name); - - save_flags(flags); - cli(); - switch (nl->connection) { - case WIC_CN_CLOSING: - dev->tbusy = 0; - case WIC_CN_NONE: - case WIC_CN_SEND: - dev->last_rx = jiffies; - rcv->state = WIC_PK_TRIGGER; - nl->connection = WIC_CN_RECEIVE; - nl->timeout_count = 0; - restore_flags(flags); - queue_task(&nl->immediate, &tq_immediate); - mark_bh(IMMEDIATE_BH); - break; - - case WIC_CN_RECEIVE: - printk("%s: receive interrupt when receiving packet\n", dev->name); - restore_flags(flags); - break; - - case WIC_CN_ERROR: - printk("%s: receive interrupt in error state\n", dev->name); - restore_flags(flags); - break; - } -} - -int -wic_rebuild_header(void *buff, struct device *dev, unsigned long dst, - struct sk_buff *skb) -{ - struct net_local *nl = (struct net_local *)dev->priv; - struct ethhdr *eth = (struct ethhdr *)buff; - int i; - - if ((dev->flags & IFF_NOARP)==0) - return nl->orig_rebuild_header(buff, dev, dst, skb); - - if (eth->h_proto != htons(ETH_P_IP)) { - printk("wic_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto); - memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - return 0; - } - - for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++) - eth->h_dest[i] = 0xfc; - memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long)); - return 0; -} - -int -wic_tx_packet(struct sk_buff *skb, struct device *dev) -{ - struct net_local *nl = (struct net_local *)dev->priv; - struct wic_local *snd = &nl->snd_data; - unsigned long flags; - - if (dev->tbusy) - return 1; - - /* If some higher layer thinks we've missed an tx-done interrupt - we are passed NULL. Caution: dev_tint() handles the cli()/sti() - itself. */ - if (skb == NULL) { - dev_tint(dev); - return 0; - } - - if (set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); - return 1; - } - - if (skb->len > dev->mtu) { - printk("%s: packet too big, %d.\n", dev->name, (int)skb->len); - dev->tbusy = 0; - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - - if (net_debug > 2) - printk("%s: send request\n", dev->name); - - save_flags(flags); - cli(); - dev->trans_start = jiffies; - snd->skb = skb; - snd->length.h = skb->len; - snd->state = WIC_PK_TRIGGER; - if (nl->connection == WIC_CN_NONE) { - nl->connection = WIC_CN_SEND; - nl->timeout_count = 0; - } - restore_flags(flags); - queue_task(&nl->immediate, &tq_immediate); - mark_bh(IMMEDIATE_BH); - - return 0; -} - -/* Open/initialize the board. This is called (in the current kernel) - sometime after booting when the 'ifconfig' program is run. - - This routine gets exclusive access to the parallel port by allocating - its IRQ line. - */ -int -wic_open(struct device *dev) -{ - struct net_local *nl = (struct net_local *)dev->priv; - unsigned long flags; - - if (dev->irq == 0) { - printk("%s: IRQ is not set. Please set it by ifconfig.\n", dev->name); - return -EAGAIN; - } - save_flags(flags); - cli(); - check_bfr(dev); - if (request_irq(dev->irq , wic_interrupt, 0, dev->name, NULL) != 0) { - sti(); - printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq); - return -EAGAIN; - } - irq2dev_map[dev->irq] = dev; - restore_flags(flags); - - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - /* Initialize the state machine. */ - nl->rcv_data.state = nl->snd_data.state = WIC_PK_DONE; - nl->rcv_data.skb = nl->snd_data.skb = NULL; - nl->connection = WIC_CN_NONE; - nl->is_deferred = 0; - - dev->interrupt = 0; - dev->start = 1; - dev->tbusy = 0; - MOD_INC_USE_COUNT; - return 0; -} - -/* The inverse routine to wic_open (). */ -int -wic_close(struct device *dev) -{ - struct net_local *nl = (struct net_local *)dev->priv; - struct wic_local *snd = &nl->snd_data; - struct wic_local *rcv = &nl->rcv_data; - - dev->tbusy = 1; - dev->start = 0; - cli(); - free_irq(dev->irq, NULL); - irq2dev_map[dev->irq] = NULL; - nl->is_deferred = 0; - nl->connection = WIC_CN_NONE; - sti(); - outb(0x00, PAR_DATA(dev)); - - snd->state = WIC_PK_DONE; - if (snd->skb) { - snd->skb->free = 1; - dev_kfree_skb(snd->skb, FREE_WRITE); - snd->skb = NULL; - } - rcv->state = WIC_PK_DONE; - if (rcv->skb) { - rcv->skb->free = 1; - kfree_skb(rcv->skb, FREE_READ); - rcv->skb = NULL; - } - - MOD_DEC_USE_COUNT; - return 0; -} - -struct enet_statistics * -wic_get_stats(struct device *dev) -{ - struct net_local *nl = (struct net_local *)dev->priv; - struct enet_statistics *r = &nl->enet_stats; - - return r; -} - -int -wic_config(struct device *dev, struct ifmap *map) -{ - if (dev->flags & IFF_UP) - return -EBUSY; - - if (map->base_addr != (unsigned long)-1 - && map->base_addr != dev->base_addr) - printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name); - - if (map->irq != (unsigned char)-1) - dev->irq = map->irq; - return 0; -} - -int -wic_ioctl(struct device *dev, struct ifreq *rq, int cmd) -{ - struct wicconf wc; - int err; - char len = 0; - unsigned long flags; - - err=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(struct wicconf)); - if (err) - return err; - memcpy_fromfs(&wc, rq->ifr_data, sizeof(struct wicconf)); - switch(wc.pcmd) { - case WIC_AYT: - strcpy(wc.data, version); - wc.len = strlen(wc.data); - memcpy_tofs(rq->ifr_data, &wc, sizeof(struct wicconf)); - /* return 0; */ - break; - case WIC_RESET: - wic_reset(dev); - return(0); - /* break; */ - case WIC_SETSN: - len = 17; - break; - case WIC_SETPS: - len = 3; - break; - case WIC_SETAF: - case WIC_SETGPF: - len = 2; - break; - case WIC_SETNET: - len = 23; - break; - case WIC_SETSYS: - len = 15; - break; - case WIC_GETVERH: - case WIC_GETNL: - case WIC_GETSN: - case WIC_CLRSTATS: - case WIC_GETSTATS: - case WIC_GETVERM: - case WIC_GETNET: - case WIC_GETSYS: - len = 1; - break; - default: - return -EOPNOTSUPP; - } - - /* Wait for lock to free */ - while (set_bit(0, (void *)&dev->tbusy) != 0); - save_flags(flags); - cli(); - - disable_irq(dev->irq); - save &= 0xef; /* disable */ - outb(save, PAR_CONTROL(dev)); - err = check_bfr(dev); - tog = 3; - err = send_cmd(dev, (unsigned char *)&wc, len); - - if (wc.pcmd & 0x40) { /* response */ - len = (char)recv_cmd_resp(dev, wc.data); - while ((len == 1) && (wc.data[0] == 0x7)) { /* controller int */ - len = (char)recv_cmd_resp(dev, wc.data); - } - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - wc.len = (len <0) ? 0 : len; - memcpy_tofs(rq->ifr_data, &wc, sizeof(struct wicconf)); - } else { - save |= 0x10; /* enable */ - outb(save, PAR_CONTROL(dev)); - enable_irq(dev->irq); - } - restore_flags(flags); - - outb(0, PAR_DATA(dev)); - dev->tbusy = 0; - return 0; -} - -int -get_byte(struct device *dev, unsigned char *c) -{ -unsigned int cx; - - cx = LOOPCNT; - while ((inb(PAR_STATUS(dev)) & 0x08) != ((tog << 3)&0x08)) { - if (--cx == 0) { - return(-TIMEOUT); - } - } - /* receive a byte of data */ - *c = inb(PAR_DATA(dev)); - tog ^= 0x01; - /* ack reception of data */ - outb(tog| save, PAR_CONTROL(dev)); - return OK; -} - -int -ack_resp(struct device *dev) -{ -unsigned int cx; - - outb(save | 0x27, PAR_CONTROL(dev)); - - /* wait for controller to remove interrupt [Ack(low), Busy(low)] */ - cx = LOOPCNT; - while ((inb(PAR_STATUS(dev)) & 0xc0) != 0x80) { - if (--cx == 0) { - return -TIMEOUT; - } - } - - outb(save | 0x22, PAR_CONTROL(dev)); - cx = LOOPCNT; - while ((inb(PAR_STATUS(dev)) & 0x08) == 0x08) { - if (--cx == 0) { - return TIMEOUT; - } - } - tog |= 0x20; - tog &= 0xfe; - return OK; -} - -void -wic_reset(struct device *dev) -{ -unsigned char stat; - - stat = inb(PAR_CONTROL(dev)); - outb(0, PAR_DATA(dev)); - outb(stat | 0x08, PAR_CONTROL(dev)); - outb(stat & 0xf7, PAR_CONTROL(dev)); - dev->tbusy = 0; - dev->interrupt = 0; - tog = 3; - save = 0; - return; -} - -int -check_bfr(struct device *dev) -{ -unsigned char c0, l; - - if ((inb(PAR_STATUS(dev)) & 0xc8) == 0x48) { - save |= 0x80; - outb(0x23| save, PAR_CONTROL(dev)); - ack_resp(dev); - get_byte(dev, &l); /* len */ - while (l--) { - get_byte(dev, &c0); - } - get_byte(dev, &c0); - save &=0x7f; - outb(0, PAR_DATA(dev)); - return -l; - } else - return (0); -} - - -int -recv_cmd_resp(struct device *dev, unsigned char *buf) -{ -unsigned char cksum = 0; -int err; -unsigned char c0 = 0; -int len; -int savelen; -unsigned int cx; -int i; - - tog &= 0xfe; - cx = LOOPCNT; - while ((inb(PAR_STATUS(dev)) & 0xc8) != 0x48) { - if (--cx == 0) { - /* clear Busy */ - outb(0, PAR_DATA(dev)); - printk("rcv_cmd_resp: timeout\n"); - return -TIMEOUT; - } - } - - /* acknowledge the interrupt */ - i = ack_resp(dev); - - /* get length */ - err = get_byte(dev, &c0); - if (err < 0) { - printk("get_byte1: failed\n"); - return(err); - } - len = c0; - savelen = len; - - /* get data */ - while(len--) { - err = get_byte(dev, &c0); - if (err < 0) { - printk("get_byte2: failed\n"); - return(err); - } - outb(0, PAR_DATA(dev)); - *buf = c0; - cksum += c0; - buf++; - } - /* get cksum */ - err = get_byte(dev, &c0); - if (err < 0) { - printk("get_byte3: failed\n"); - return(err); - } - if (cksum != c0) { - printk("cksum failed\n"); - return(-3); - } - /* get trailing byte, if any... */ - get_byte(dev, &c0); - return(savelen); -} - -int -send_byte(struct device *dev, unsigned char c) -{ -unsigned int cx; - - cx = LOOPCNT; - while ((inb(PAR_STATUS(dev)) & 0x80) == ((tog<<7) & 0x80)) { - if (--cx == 0) { - return(-TIMEOUT); - } - } - outb(c, PAR_DATA(dev)); - outb(save |tog, PAR_CONTROL(dev)); - tog ^= 0x01; - return OK; -} - - -int -send_cmd(struct device *dev, unsigned char *cmd, char len) -{ -unsigned char cksum = 0; -int err = 0; -unsigned int cx; - - /* interrupt controller */ - outb(save | 0x04, PAR_CONTROL(dev)); - /* wait for ACK */ - cx = LOOPCNT; - while ((inb(PAR_STATUS(dev)) & 0xe8) != 0xc0) { - if (--cx == 0) - return -TIMEOUT; - if (cx == 10) - outb(0x02, PAR_CONTROL(dev)); - } - /* cmd coming... */ - outb(save | 0x02, PAR_CONTROL(dev)); - /* send length byte */ - err = send_byte(dev, (unsigned char)len); - - /* send data */ - while (len--) { - err = send_byte(dev, *cmd); - if (err < 0) { - return err; - } - cksum += *cmd; - cmd++; - } - - /* send cksum byte */ - err = send_byte(dev, cksum); - if (err < 0) - return err; - - cx = LOOPCNT; - while ((inb(PAR_STATUS(dev)) & 0x80) == ((tog <<7)&0x80)) { - if (--cx == 0) - return -TIMEOUT; - } - save |= 0x80; - outb(save | 0x23, PAR_CONTROL(dev)); - outb(0, PAR_DATA(dev)); - return OK; -} - -#ifdef MODULE -struct device dev_wic0 = -{ - "wic0" /*"wic"*/, - 0, 0, 0, 0, /* memory */ - 0x3BC, 5, /* base, irq */ - 0, 0, 0, NULL, wic_init -}; - -struct device dev_wic1 = -{ - "wic1" /*"wic"*/, - 0, 0, 0, 0, /* memory */ - 0x378, 7, /* base, irq */ - 0, 0, 0, NULL, wic_init -}; - -struct device dev_wic2 = -{ - "wic2" /*"wic"*/, - 0, 0, 0, 0, /* memory */ - 0x278, 2, /* base, irq */ - 0, 0, 0, NULL, wic_init -}; - -int -init_module(void) -{ - int devices=0; - - if (register_netdev(&dev_wic0) != 0) - devices++; - if (register_netdev(&dev_wic1) != 0) - devices++; - if (register_netdev(&dev_wic2) != 0) - devices++; - if (devices == 0) - return -EIO; - return 0; -} - -void -cleanup_module(void) -{ - if (dev_wic0.priv) { - unregister_netdev(&dev_wic0); - release_region(PAR_DATA(&dev_wic0), 3); - kfree_s(dev_wic0.priv, sizeof(struct net_local)); - dev_wic0.priv = NULL; - } - if (dev_wic1.priv) { - unregister_netdev(&dev_wic1); - release_region(PAR_DATA(&dev_wic1), 3); - kfree_s(dev_wic1.priv, sizeof(struct net_local)); - dev_wic1.priv = NULL; - } - if (dev_wic2.priv) { - unregister_netdev(&dev_wic2); - release_region(PAR_DATA(&dev_wic2), 3); - kfree_s(dev_wic2.priv, sizeof(struct net_local)); - dev_wic2.priv = NULL; - } -} -#endif /* MODULE */ - -/* - * Local variables: - * compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c wic.c" - * End: - */ diff -u --recursive --new-file v2.0.35/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v2.0.35/linux/drivers/pci/pci.c Mon Jul 13 13:46:30 1998 +++ linux/drivers/pci/pci.c Sun Nov 15 10:33:04 1998 @@ -262,6 +262,8 @@ DEVICE( WINBOND, WINBOND_82C105, "SL82C105"), DEVICE( WINBOND, WINBOND_83C553, "W83C553"), DEVICE( DATABOOK, DATABOOK_87144, "DB87144"), + DEVICE( PLX, PLX_SPCOM200, "SPCom 200 PCI serial I/O"), + DEVICE( PLX, PLX_9050, "PLX9050 PCI <-> IOBus Bridge"), DEVICE( PLX, PLX_9080, "PCI9080 I2O"), DEVICE( MADGE, MADGE_MK2, "Smart 16/4 BM Mk2 Ringnode"), DEVICE( 3COM, 3COM_3C339, "3C339 TokenRing"), @@ -400,6 +402,7 @@ DEVICE( LITEON, LITEON_LNE100TX,"LNE100TX"), DEVICE( NP, NP_PCI_FDDI, "NP-PCI"), DEVICE( ATT, ATT_L56XMF, "L56xMF"), + DEVICE( SPECIALIX, SPECIALIX_IO8, "IO8+/PCI"), DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"), DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"), DEVICE( AURAVISION, AURAVISION_VXP524,"VXP524"), @@ -431,6 +434,7 @@ DEVICE( OPTIBASE, OPTIBASE_VPLEX, "VideoPlex"), DEVICE( OPTIBASE, OPTIBASE_VPLEXCC,"VideoPlex CC"), DEVICE( OPTIBASE, OPTIBASE_VQUEST,"VideoQuest"), + DEVICE( ASIX, ASIX_88140, "88140"), DEVICE( SATSAGEM, SATSAGEM_PCR2101,"PCR2101 DVB receiver"), DEVICE( SATSAGEM, SATSAGEM_TELSATTURBO,"Telsat Turbo DVB"), DEVICE( ENSONIQ, ENSONIQ_AUDIOPCI,"AudioPCI"), @@ -501,6 +505,9 @@ DEVICE( INTEL, INTEL_82443BX_0,"440BX - 82443BX Host"), DEVICE( INTEL, INTEL_82443BX_1,"440BX - 82443BX AGP"), DEVICE( INTEL, INTEL_82443BX_2,"440BX - 82443BX Host (no AGP)"), + DEVICE( INTEL, INTEL_82443GX_0,"440GX - 82443GX Host"), + DEVICE( INTEL, INTEL_82443GX_1,"440GX - 82443GX AGP"), + DEVICE( INTEL, INTEL_82443GX_2,"440GX - 82443GX Host (no AGP)"), DEVICE( INTEL, INTEL_P6, "Orion P6"), DEVICE( INTEL, INTEL_82450GX, "82450GX Orion P6"), DEVICE( KTI, KTI_ET32P2, "ET32P2"), @@ -522,6 +529,10 @@ DEVICE( ADAPTEC, ADAPTEC_7883, "AIC-7883U"), DEVICE( ADAPTEC, ADAPTEC_7884, "AIC-7884U"), DEVICE( ADAPTEC, ADAPTEC_1030, "ABA-1030 DVB receiver"), + DEVICE( ADAPTEC2, ADAPTEC2_2940U2, "AHA-2940U2"), + DEVICE( ADAPTEC2, ADAPTEC2_7890, "AIC-7890/1"), + DEVICE( ADAPTEC2, ADAPTEC2_3940U2, "AHA-3940U2"), + DEVICE( ADAPTEC2, ADAPTEC2_7896, "AIC-7896/7"), DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"), DEVICE( TIGERJET, TIGERJET_300, "Tiger300 ISDN"), DEVICE( ARK, ARK_STING, "Stingray"), @@ -812,6 +823,8 @@ case PCI_VENDOR_ID_3DFX: return "3Dfx"; case PCI_VENDOR_ID_SIGMADES: return "Sigma Designs"; case PCI_VENDOR_ID_OPTIBASE: return "Optibase"; + case PCI_VENDOR_ID_NVIDIA_SGS: return "NVidia/SGS Thomson"; + case PCI_VENDOR_ID_ENSONIQ: return "Ensoniq"; case PCI_VENDOR_ID_SYMPHONY: return "Symphony"; case PCI_VENDOR_ID_TEKRAM: return "Tekram"; case PCI_VENDOR_ID_3DLABS: return "3Dlabs"; @@ -821,8 +834,11 @@ case PCI_VENDOR_ID_INTEL: return "Intel"; case PCI_VENDOR_ID_KTI: return "KTI"; case PCI_VENDOR_ID_ADAPTEC: return "Adaptec"; + case PCI_VENDOR_ID_ADAPTEC2: return "Adaptec"; case PCI_VENDOR_ID_ATRONICS: return "Atronics"; case PCI_VENDOR_ID_ARK: return "ARK Logic"; + case PCI_VENDOR_ID_ASIX: return "ASIX"; + case PCI_VENDOR_ID_LITEON: return "Lite-on"; default: return "Unknown vendor"; } } diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c --- v2.0.35/linux/drivers/scsi/BusLogic.c Mon Jul 13 13:46:31 1998 +++ linux/drivers/scsi/BusLogic.c Sun Nov 15 10:33:05 1998 @@ -26,8 +26,8 @@ */ -#define BusLogic_DriverVersion "2.0.14" -#define BusLogic_DriverDate "29 April 1998" +#define BusLogic_DriverVersion "2.0.15" +#define BusLogic_DriverDate "17 August 1998" #include @@ -1190,7 +1190,8 @@ BusLogic_ProbeInfoCount - FlashPointCount; memcpy(SavedProbeInfo, BusLogic_ProbeInfoList, - sizeof(BusLogic_ProbeInfoList)); + BusLogic_ProbeInfoCount + * sizeof(BusLogic_ProbeInfo_T)); memcpy(&BusLogic_ProbeInfoList[0], &SavedProbeInfo[FlashPointCount], MultiMasterCount * sizeof(BusLogic_ProbeInfo_T)); @@ -3149,25 +3150,25 @@ HostAdapter->TargetStatistics[CCB->TargetID] .CommandsCompleted++; if (BusLogic_GlobalOptions.TraceErrors) - { - int i; - BusLogic_Notice("CCB #%ld Target %d: Result %X Host " - "Adapter Status %02X " - "Target Status %02X\n", - HostAdapter, CCB->SerialNumber, - CCB->TargetID, Command->result, - CCB->HostAdapterStatus, - CCB->TargetDeviceStatus); - BusLogic_Notice("CDB ", HostAdapter); - for (i = 0; i < CCB->CDB_Length; i++) - BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]); - BusLogic_Notice("\n", HostAdapter); - BusLogic_Notice("Sense ", HostAdapter); - for (i = 0; i < CCB->SenseDataLength; i++) - BusLogic_Notice(" %02X", HostAdapter, - Command->sense_buffer[i]); - BusLogic_Notice("\n", HostAdapter); - } + { + int i; + BusLogic_Notice("CCB #%ld Target %d: Result %X Host " + "Adapter Status %02X " + "Target Status %02X\n", + HostAdapter, CCB->SerialNumber, + CCB->TargetID, Command->result, + CCB->HostAdapterStatus, + CCB->TargetDeviceStatus); + BusLogic_Notice("CDB ", HostAdapter); + for (i = 0; i < CCB->CDB_Length; i++) + BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]); + BusLogic_Notice("\n", HostAdapter); + BusLogic_Notice("Sense ", HostAdapter); + for (i = 0; i < CCB->SenseDataLength; i++) + BusLogic_Notice(" %02X", HostAdapter, + Command->sense_buffer[i]); + BusLogic_Notice("\n", HostAdapter); + } } break; } @@ -4219,7 +4220,6 @@ BusLogic_TargetStatistics_T *TargetStatistics; int TargetID, Length; char *Buffer; - if (WriteFlag) return 0; for (HostAdapter = BusLogic_FirstRegisteredHostAdapter; HostAdapter != NULL; HostAdapter = HostAdapter->Next) @@ -4231,6 +4231,14 @@ return 0; } TargetStatistics = HostAdapter->TargetStatistics; + if (WriteFlag) + { + HostAdapter->ExternalHostAdapterResets = 0; + HostAdapter->HostAdapterInternalErrors = 0; + memset(TargetStatistics, 0, + BusLogic_MaxTargetDevices * sizeof(BusLogic_TargetStatistics_T)); + return 0; + } Buffer = HostAdapter->MessageBuffer; Length = HostAdapter->MessageBufferLength; Length += sprintf(&Buffer[Length], "\n\ diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.0.35/linux/drivers/scsi/Config.in Mon Jul 13 13:46:31 1998 +++ linux/drivers/scsi/Config.in Sun Nov 15 10:33:05 1998 @@ -30,6 +30,7 @@ dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI 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 dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then bool ' Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/FlashPoint.c linux/drivers/scsi/FlashPoint.c --- v2.0.35/linux/drivers/scsi/FlashPoint.c Mon Jul 13 13:46:31 1998 +++ linux/drivers/scsi/FlashPoint.c Sun Nov 15 10:33:05 1998 @@ -3165,11 +3165,11 @@ if(RD_HARPOON(ioport + hp_page_ctrl) & BIOS_SHADOW) { - pAdapterInfo->ai_FlashRomSize = 64 * 1024; /* 64k Rom */ + pAdapterInfo->ai_FlashRomSize = 64 * 1024; /* 64k ROM */ } else { - pAdapterInfo->ai_FlashRomSize = 32 * 1024; /* 32k Rom */ + pAdapterInfo->ai_FlashRomSize = 32 * 1024; /* 32k ROM */ } pAdapterInfo->ai_stateinfo |= (FAST20_ENA | TAG_QUEUE_ENA); @@ -4944,7 +4944,7 @@ * * Function: Sccb_bad_isr * - * Description: Some type of interrupt has occured which is slightly + * Description: Some type of interrupt has occurred which is slightly * out of the ordinary. We will now decode it fully, in * this routine. This is broken up in an attempt to save * processing time. @@ -7038,7 +7038,7 @@ * * Function: sssyncv * - * Description: Write the desired value to the Sync Regisiter for the + * Description: Write the desired value to the Sync Register for the * ID specified. * *---------------------------------------------------------------------*/ diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.0.35/linux/drivers/scsi/Makefile Mon Jul 13 13:46:31 1998 +++ linux/drivers/scsi/Makefile Sun Nov 15 10:33:05 1998 @@ -363,6 +363,13 @@ endif endif +ifeq ($(CONFIG_SCSI_MEGARAID),y) +L_OBJS += megaraid.o +else + ifeq ($(CONFIG_SCSI_MEGARAID),m) + M_OBJS += megaraid.o + endif +endif ifeq ($(CONFIG_BLK_DEV_IDESCSI),y) L_OBJS += ide-scsi.o @@ -376,7 +383,7 @@ gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h $(CC) $(CFLAGS) $(GDTH) -c gdth.c -aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h +aic7xxx.o: aic7xxx.c aic7xxx_seq.c aic7xxx_reg.h $(CC) $(CFLAGS) -c -o $@ aic7xxx.c seagate.o: seagate.c @@ -394,6 +401,9 @@ g_NCR5380.o: g_NCR5380.c $(CC) $(CFLAGS) -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" -c g_NCR5380.c + +megaraid.o: megaraid.c + $(CC) $(CFLAGS) -c megaraid.c scsi_mod.o: $(MX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \ scsicam.o scsi_proc.o diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic --- v2.0.35/linux/drivers/scsi/README.BusLogic Mon Jul 13 13:46:31 1998 +++ linux/drivers/scsi/README.BusLogic Sun Nov 15 10:33:05 1998 @@ -1,10 +1,11 @@ BusLogic MultiMaster and FlashPoint SCSI Driver for Linux - Version 2.0.14 for Linux 2.0 + Version 2.0.15 for Linux 2.0 + Version 2.1.15 for Linux 2.1 PRODUCTION RELEASE - 29 April 1998 + 17 August 1998 Leonard N. Zubkoff Dandelion Digital @@ -80,7 +81,7 @@ 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 Technical Support can be reached by electronic +http://www.mylex.com. Mylex HBA Technical Support can be reached by electronic mail at techsup@mylex.com, 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. @@ -584,16 +585,16 @@ DRIVER INSTALLATION -This distribution was prepared for Linux kernel version 2.0.33, but should be +This distribution was prepared for Linux kernel version 2.0.35, but should be compatible with 2.0.4 or any later 2.0 series kernel. To install the new BusLogic SCSI driver, you may use the following commands, replacing "/usr/src" with wherever you keep your Linux kernel source tree: cd /usr/src - tar -xvzf BusLogic-2.0.14.tar.gz + tar -xvzf BusLogic-2.0.15.tar.gz mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi - patch -p < BusLogic.patch + patch -p0 < BusLogic.patch (only for 2.0.33 and below) cd linux make config make depend diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.Mylex linux/drivers/scsi/README.Mylex --- v2.0.35/linux/drivers/scsi/README.Mylex Mon Jul 13 13:46:31 1998 +++ linux/drivers/scsi/README.Mylex Sun Nov 15 10:33:05 1998 @@ -1,6 +1,5 @@ Please see the file README.BusLogic for information about Linux support for Mylex (formerly BusLogic) MultiMaster and FlashPoint SCSI Host Adapters. -The Mylex DAC960 PCI RAID Controllers are not supported at the present time, -but work on a Linux driver for the DAC960 is in progress. Please consult +The Mylex DAC960 PCI RAID Controllers are now supported. Please consult http://www.dandelion.com/Linux/ for further information on the DAC960 driver. diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx --- v2.0.35/linux/drivers/scsi/README.aic7xxx Sun Nov 15 10:49:43 1998 +++ linux/drivers/scsi/README.aic7xxx Sun Nov 15 10:33:06 1998 @@ -21,15 +21,24 @@ AHA-2940W AHA-2940U AHA-2940UW - AHA-2940AU + AHA-2940AU + AHA-2940U2W + AHA-2940U2 + AHA-2940U2B + AHA-2940U2BOEM AHA-2944D AHA-2944WD AHA-2944UD AHA-2944UWD + AHA-2950U2 + AHA-2950U2W + AHA-2950U2B AHA-3940 AHA-3940U AHA-3940W AHA-3940UW + AHA-3940U2W + AHA-3950U2B AHA-3985 AHA-3985U AHA-3985W @@ -42,13 +51,14 @@ AIC-786x AIC-787x AIC-788x - AIC-7895 + AIC-789x Bus Types ---------------------------- W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s. U - Ultra SCSI, transfer rates up to 40MB/s. + U2- Ultra 2 SCSI, transfer rates up to 80MB/s. D - Differential SCSI. T - Twin Channel SCSI. Up to 14 SCSI devices. @@ -72,7 +82,7 @@ (Original Linux FTP/patch maintainer) Jess Johnson jester@frenzy.com (AIC7xxx FAQ author) - Doug Ledford dledford@dialnet.net + Doug Ledford dledford@redhat.com (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer) Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original @@ -100,40 +110,45 @@ Boot Command line options ------------------------------ - "aic7xxx=no_reset" - Eliminate the SCSI reset delay during startup. - Some SCSI devices need some extra time to reset. - "aic7xxx=reverse_scan" - Have the driver register the SCSI cards in the - reverse of the normal order. This may help those people who have more - than one PCI Adaptec controller force the correct controller to be - scsi0 under linux so that their boot hard drive is also sda under - linux + "aic7xxx=no_reset" - Eliminate the SCSI bus reset during startup. + Some SCSI devices need the initial reset that this option disables + in order to work. If you have problems at bootup, please make sure + you aren't using this option. + + "aic7xxx=reverse_scan" - Certain PCI motherboards scan for devices at + bootup by scanning from the highest numbered PCI device to the + lowest numbered PCI device, others do just the opposite and scan + from lowest to highest numbered PCI device. There is no reliable + way to autodetect this ordering. So, we default to the most common + order, which is lowest to highest. Then, in case your motherboard + scans from highest to lowest, we have this option. If your BIOS + finds the drives on controller A before controller B but the linux + kernel finds your drives on controller B before A, then you should + use this option. + "aic7xxx=extended" - Force the driver to detect extended drive translation on your controller. This helps those people who have cards without a SEEPROM make sure that linux and all other operating systems think the same way about your hard drives. + "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel to use the correct IRQ type for your card. This only applies to EISA based controllers. On these controllers, 0 is for Edge triggered interrupts, and 1 is for Level triggered interrupts. If you aren't sure or don't know which IRQ trigger type your EISA card uses, then let the kernel autodetect the trigger type. + "aic7xxx=verbose" - This option can be used in one of two ways. If you - simply specify aic7xxx=verbose, then the kernel will automatically pick - the default set of verbose messages for you to see. Alternatively, you - can specify the command as "aic7xxx=verbose:0xXXXX" where the X entries - are replaced with hexadecimal digits. This option is a bit field type - option. For a full listing of the available options, search for the - #define VERBOSE_xxxxxx lines in the aic7xxx.c file. If you want verbose - messages, then it is recommended that you simply use the aic7xxx=verbose - variant of this command. - "aic7xxx=7895_irq_hack:x" - This option enables some work around code to - fix a bug in the Tyan Thunder II motherboard BIOS. The BIOS - incorrectly sets the IRQs on the two channels of the 7895 to two - different values even though the motherboard hardware doesn't support - this mode of operation. The valid values for x are: 0 to force - both channels to use the IRQ assigned to Channel A, 1 to force both - channels to use the IRQ assigned to Channel B, and -1 will disable - this horrible abomination of a hack. The default is disabled (-1). + simply specify aic7xxx=verbose, then the kernel will automatically + pick the default set of verbose messages for you to see. + Alternatively, you can specify the command as + "aic7xxx=verbose:0xXXXX" where the X entries are replaced with + hexadecimal digits. This option is a bit field type option. For + a full listing of the available options, search for the + #define VERBOSE_xxxxxx lines in the aic7xxx.c file. If you want + verbose messages, then it is recommended that you simply use the + aic7xxx=verbose variant of this command. + "aic7xxx=pci_parity:x" - This option controls whether or not the driver enables PCI parity error checking on the PCI bus. By default, this checking is disabled. To enable the checks, simply specify pci_parity @@ -145,6 +160,145 @@ NOTE: In order to get Even PCI parity checking, you must use the version of the option that does not include the : and a number at the end (unless you want to enter exactly 2^32 - 1 as the number). + + "aic7xxx=no_probe" - This option will disable the probing for any VLB + based 2842 controllers and any EISA based controllers. This is + needed on certain newer motherboards where the normal EISA I/O ranges + have been claimed by other PCI devices. Probing on those machines + will often result in the machine crashing or spontaneously rebooting + during startup. Examples of machines that need this are the + Dell PowerEdge 6300 machines. + + "aic7xxx=panic_on_abort" - This option is for debugging and will cause + the driver to panic the linux kernel and freeze the system the first + time the drivers abort or reset routines are called. This is most + helpful when some problem causes infinite reset loops that scroll too + fast to see. By using this option, you can write down what the errors + actually are and send that information to me so it can be fixed. + + "aic7xxx=dump_card" - This option will print out the *entire* set of + configuration registers on the card during the init sequence. This + is a debugging aid used to see exactly what state the card is in + when we finally finish our initialization routines. If you don't + have documentation on the chipsets, this will do you absolutely + no good unless you are simply trying to write all the information + down in order to send it to me. + + "aic7xxx=dump_sequencer" - This is the same as the above options except + that instead of dumping the register contents on the card, this + option dumps the contents of the sequencer program RAM. This gives + the ability to verify that the instructions downloaded to the + card's sequencer are indeed what they are suppossed to be. Again, + unless you have documentation to tell you how to interpret these + numbers, then it is totally useless. + + "aic7xxx=override_term:0xffffffff" - This option is used to force the + termination on your SCSI controllers to a particular setting. This + is a bit mask variable that applies for up to 8 aic7xxx SCSI channels. + Each channel gets 4 bits, divided as follows: + bit 3 2 1 0 + | | | Enable/Disable Single Ended Low Byte Termination + | | En/Disable Single Ended High Byte Termination + | En/Disable Low Byte LVD Termination + En/Disable High Byte LVD Termination + + The upper 2 bits that deal with LVD termination only apply to Ultra2 + controllers. Futhermore, due to the current Ultra2 controller + designs, these bits are tied together such that setting either bit + enables both low and high byte LVD termination. It is not possible + to only set high or low byte LVD termination in this manner. This is + an artifact of the BIOS definition on Ultra2 controllers. For other + controllers, the only important bits are the two lowest bits. Setting + the higher bits on non-Ultra2 controllers has no effect. A few + examples of how to use this option: + + Enable low and high byte termination on a non-ultra2 controller that + is the first aic7xxx controller (the correct bits are 0011), + aic7xxx=override_term:0x3 + + Enable all termination on the third aic7xxx controller, high byte + termination on the second aic7xxx controller, and low and high byte + SE termination on the first aic7xxx controller + (bits are 1111 0010 0011), + aic7xxx=override_term:0xf23 + + No attempt has been made to make this option non-cryptic. It really + shouldn't be used except in dire circumstances, and if that happens, + I'm probably going to be telling you what to set this to anyway :) + + "aic7xxx=stpwlev:0xffffffff" - This option is used to control the STPWLEV + bit in the DEVCONFIG PCI register. Currently, this is one of the + very few registers that we have absolutely *no* way of detecting + what the variable should be. It depends entirely on how the chipset + and external terminators were coupled by the card/motherboard maker. + Further, a chip reset (at power up) always sets this bit to 0. If + there is no BIOS to run on the chipset/card (such as with a 2910C + or a motherboard controller with the BIOS totally disabled) then + the variable may not get set properly. Of course, if the proper + setting was 0, then that's what it would be after the reset, but if + the proper setting is actually 1.....you get the picture. Now, since + we can't detect this at all, I've added this option to force the + setting. If you have a BIOS on your controller then you should never + need to use this option. However, if you are having lots of SCSI + reset problems and can't seem to get them knocked out, this may help. + + Here's a test to know for certain if you need this option. Make + a boot floppy that you can use to boot your computer up and that + will detect the aic7xxx controller. Next, power down your computer. + While it's down, unplug all SCSI cables from your Adaptec SCSI + controller. Boot the system back up to the Adaptec EZ-SCSI BIOS + and then make sure that termination is enabled on your adapter (if + you have an Adaptec BIOS of course). Next, boot up the floppy you + made and wait for it to detect the aic7xxx controller. If the kernel + finds the controller fine, says scsi : x hosts and then tries to + detect your devices like normal, up to the point where it fails to + mount your root file system and panics, then you're fine. If, on + the other hand, the system goes into an infinite reset loop, then + you need to use this option and/or the previous option to force the + proper termination settings on your controller. If this happens, + then you next need to figure out what your settings should be. + + To find the correct settings, power your machine back down, connect + back up the SCSI cables, and boot back into your machine like normal. + However, boot with the aic7xxx=verbose:0x39 option. Record the + initial DEVCONFIG values for each of your aic7xxx controllers as + they are listed, and also record what the machine is detecting as + the proper termination on your controllers. NOTE: the order in + which the initial DEVCONFIG values are printed out is not gauranteed + to be the same order as the SCSI controllers are registered. The + above option and this option both work on the order of the SCSI + controllers as they are registered, so make sure you match the right + DEVCONFIG values with the right controllers if you have more than + one aic7xxx controller. + + Once you have the detected termination settings and the initial + DEVCONFIG values for each controller, then figure out what the + termination on each of the controllers *should* be. Hopefully, that + part is correct, but it could possibly be wrong if there is + bogus cable detection logic on your controller or something similar. + If all the controllers have the correct termination settings, then + don't set the aic7xxx=override_term variable at all, leave it alone. + Next, on any controllers that go into an infinite reset loop when + you unplug all the SCSI cables, get the starting DEVCONFIG value. + If the initial DEVCONFIG value is divisible by 2, then the correct + setting for that controller is 0. If it's an odd number, then + the correct setting for that controller is 1. For any other + controllers that didn't have an infinite reset problem, then reverse + the above options. If DEVCONFIG was even, then the correct setting + is 1, if not then the correct setting is 0. + + Now that you know what the correct setting was for each controller, + we need to encode that into the aic7xxx=stpwlev:0x... variable. + This variable is a bit field encoded variable. Bit 0 is for the first + aic7xxx controller, bit 1 for the next, etc. Put all these bits + together and you get a number. For example, if the third aic7xxx + needed a 1, but the second and first both needed a 0, then the bits + would be 100 in binary. This then translates to 0x04. You would + therefore set aic7xxx=stpwlev:0x04. This is fairly standard binary + to hexadecimal conversions here. If you aren't up to speed on the + binary->hex conversion then send an email to the aic7xxx mailing + list and someone can help you out. + "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable tagged queueing on specific devices. As of driver version 5.0.6, we now globally enable tagged queueing by default, but we also disable @@ -212,8 +366,8 @@ aic7xxx=verbose,extended,irq_trigger:1 - The only requirement is that individual options be separated by a comma on - the command line. + The only requirement is that individual options be separated by a comma or + a period on the command line. Module Loading command options ------------------------------ @@ -258,14 +412,38 @@ the system. Each file presents the current configuration and transfer statistics (enabled with #define in aic7xxx.c) for each controller. - Thanks to Michael Neuffer for for his upper-level SCSI help, and + Thanks to Michael Neuffer for his upper-level SCSI help, and Matthew Jacob for statistics support. + Debugging the driver + ------------------------------ + Should you have problems with this driver, and would like some help in + getting them solved, there are a couple debugging items built into + the driver to facilitate getting the needed information from the system. + In general, I need a complete description of the problem, with as many + logs as possible concerning what happens. To help with this, there is + a command option aic7xxx=panic_on_abort. This option, when set, forces + the driver to panic the kernel on the first SCSI abort issued by the + mid level SCSI code. If your system is going to reset loops and you + can't read the screen, then this is what you need. Not only will it + stop the system, but it also prints out a large amount of state + information in the process. Second, if you specify the option + "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much + information as it runs that you won't be able to see anything. + However, this can actually be very usefull if your machine simply + locks up when trying to boot, since it will pin-point what was last + happening (in regards to the aic7xxx driver) immediately prior to + the lockup. This is really only usefull if your machine simply can + not boot up successfully. If you can get your machine to run, then + this will produce far too much information. + FTP sites ------------------------------ - ftp://ftp.dialnet.net/pub/linux/aic7xxx/ + ftp://ftp.redhat.com/pub/aic/ - Primary site for Doug Ledford developed driver releases - - US Linux mirror of Teleport site + ftp://ftp.dialnet.net/pub/linux/aic7xxx + - Temporary mirror of the redhat.com ftp site while people + get used to the new address ftp://ftp.pcnet.com/users/eischen/Linux/ - Dan Eischen's driver distribution area ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/ diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.in2000 linux/drivers/scsi/README.in2000 --- v2.0.35/linux/drivers/scsi/README.in2000 Mon Jul 13 13:46:31 1998 +++ linux/drivers/scsi/README.in2000 Sun Nov 15 10:33:06 1998 @@ -1,4 +1,19 @@ +UPDATE NEWS: version 1.33 - 26 Aug 98 + + Interrupt management in this driver has become, over + time, increasingly odd and difficult to explain - this + has been mostly due to my own mental inadequacies. In + recent kernels, it has failed to function at all when + compiled for SMP. I've fixed that problem, and after + taking a fresh look at interrupts in general, greatly + reduced the number of places where they're fiddled + with. Done some heavy testing and it looks very good. + The driver now makes use of the __initfunc() and + __initdata macros to save about 4k of kernel memory. + Once again, the same code works for both 2.0.xx and + 2.1.xx kernels. + UPDATE NEWS: version 1.32 - 28 Mar 98 Removed the check for legal IN2000 hardware versions: diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx --- v2.0.35/linux/drivers/scsi/README.ncr53c8xx Mon Jul 13 13:46:31 1998 +++ linux/drivers/scsi/README.ncr53c8xx Sun Nov 15 10:33:06 1998 @@ -823,7 +823,7 @@ For 810A, 860, 825A and 875 scsi chips, this option enables support of features that reduce load of PCI bus and memory accesses during scsi transfer processing: burst op-code fetch, read multiple, - read line, prefetch, cache line line, write and invalidate, + read line, prefetch, cache line, write and invalidate, burst 128 (875 only), large dma fifo (875 only), offset 16 (875 only). Can be changed by the following boot setup command: ncr53c8xx=specf:n diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c --- v2.0.35/linux/drivers/scsi/aha152x.c Sun Nov 15 10:49:43 1998 +++ linux/drivers/scsi/aha152x.c Sun Nov 15 10:33:07 1998 @@ -551,6 +551,8 @@ } signatures[] = { { "Adaptec AHA-1520 BIOS", 0x102e, 21 }, /* Adaptec 152x */ + { "Adaptec AHA-1520B", 0x0b, 17 }, /* Adaptec 152x rev B */ + { "Adaptec AHA-1520B/1522B", 0x3e20, 23 }, /* Adaptec 1520B/1522B */ { "Adaptec ASW-B626 BIOS", 0x1029, 21 }, /* on-board controller */ { "Adaptec BIOS: ASW-B626", 0x0f, 22 }, /* on-board controller */ { "Adaptec ASW-B626 S2", 0x2e6c, 19 }, /* on-board controller */ diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c --- v2.0.35/linux/drivers/scsi/aha1542.c Sun Nov 15 10:49:43 1998 +++ linux/drivers/scsi/aha1542.c Sun Nov 15 10:33:07 1998 @@ -16,6 +16,8 @@ * Modified by Mike McLagan * Recognise extended mode on AHA1542CP, different bit than 1542CF * 1-Jan-97 + * Modified by Bjorn L. Thordarson and Einar Thor Einarsson + * Recognize that DMA0 is valid DMA channel -- 13-Jul-98 */ #include @@ -89,13 +91,6 @@ * Factory default is 5 MB/s. */ - -/* The DMA-Controller. We need to fool with this because we want to - be able to use the aha1542 without having to have the bios enabled */ -#define DMA_MODE_REG 0xd6 -#define DMA_MASK_REG 0xd4 -#define CASCADE 0xc0 - #define BIOS_TRANSLATION_1632 0 /* Used by some old 1542A boards */ #define BIOS_TRANSLATION_6432 1 /* Default case these days */ #define BIOS_TRANSLATION_25563 2 /* Big disk case */ @@ -748,8 +743,8 @@ *dma_chan = 5; break; case 0x01: - printk("DMA priority 0 not available for Adaptec driver\n"); - return -1; + *dma_chan = 0; + break; case 0: /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel. Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */ @@ -1019,9 +1014,9 @@ goto unregister; } - if (dma_chan >= 5) { - outb((dma_chan - 4) | CASCADE, DMA_MODE_REG); - outb(dma_chan - 4, DMA_MASK_REG); + if (dma_chan == 0 || dma_chan >= 5) { + set_dma_mode(dma_chan, DMA_MODE_CASCADE); + enable_dma(dma_chan); } } aha_host[irq_level - 9] = shpnt; diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg --- v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.reg Mon Jul 13 13:46:32 1998 +++ linux/drivers/scsi/aic7xxx/aic7xxx.reg Sun Nov 15 10:33:07 1998 @@ -1,7 +1,7 @@ /* * Aic7xxx register and scratch ram definitions. * - * Copyright (c) 1994-1997 Justin Gibbs. + * Copyright (c) 1994-1998 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -161,6 +161,7 @@ access_mode RW bit WIDEXFER 0x80 /* Wide transfer control */ mask SXFR 0x70 /* Sync transfer rate */ + mask SXFR_ULTRA2 0x7f /* Sync transfer rate */ mask SOFS 0x0f /* Sync offset */ } @@ -174,6 +175,13 @@ access_mode RW mask TID 0xf0 /* Target ID mask */ mask OID 0x0f /* Our ID mask */ + /* + * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book) + * The aic7890/91 allow an offset of up to 127 transfers in both wide + * and narrow mode. + */ + alias SCSIOFFSET + mask SOFS_ULTRA2 0x7f /* Sync offset U2 chips */ } /* @@ -227,14 +235,15 @@ register SSTAT0 { address 0x00b access_mode RO - bit TARGET 0x80 /* Board acting as target */ - bit SELDO 0x40 /* Selection Done */ - bit SELDI 0x20 /* Board has been selected */ - bit SELINGO 0x10 /* Selection In Progress */ - bit SWRAP 0x08 /* 24bit counter wrap */ - bit SDONE 0x04 /* STCNT = 0x000000 */ - bit SPIORDY 0x02 /* SCSI PIO Ready */ - bit DMADONE 0x01 /* DMA transfer completed */ + bit TARGET 0x80 /* Board acting as target */ + bit SELDO 0x40 /* Selection Done */ + bit SELDI 0x20 /* Board has been selected */ + bit SELINGO 0x10 /* Selection In Progress */ + bit SWRAP 0x08 /* 24bit counter wrap */ + bit IOERR 0x08 /* LVD Tranceiver mode changed */ + bit SDONE 0x04 /* STCNT = 0x000000 */ + bit SPIORDY 0x02 /* SCSI PIO Ready */ + bit DMADONE 0x01 /* DMA transfer completed */ } /* @@ -276,6 +285,7 @@ address 0x00d access_mode RO bit OVERRUN 0x80 + bit EXP_ACTIVE 0x10 /* SCSI Expander Active */ mask SFCNT 0x1f } @@ -290,14 +300,13 @@ } /* - * SCSI Test Control (p. 3-27) + * SCSI ID for the aic7890/91 chips */ -register SCSITEST { +register SCSIID_ULTRA2 { address 0x00f access_mode RW - bit RQAKCNT 0x04 - bit CNTRTEST 0x02 - bit CMODE 0x01 + mask TID 0xf0 /* Target ID mask */ + mask OID 0x0f /* Our ID mask */ } /* @@ -312,6 +321,7 @@ bit ENSELDI 0x20 bit ENSELINGO 0x10 bit ENSWRAP 0x08 + bit ENIOERR 0x08 /* LVD Tranceiver mode changes */ bit ENSDONE 0x04 bit ENSPIORDY 0x02 bit ENDMADONE 0x01 @@ -424,7 +434,10 @@ bit DIAGLEDON 0x40 /* Aic78X0 only */ bit AUTOFLUSHDIS 0x20 bit SELBUSB 0x08 + bit ENAB40 0x08 /* LVD transceiver active */ + bit ENAB20 0x04 /* SE/HVD transceiver active */ bit SELWIDE 0x02 + bit XCVR 0x01 /* External transceiver active */ } /* @@ -547,6 +560,19 @@ bit ENABLE 0x01 } +register DSCOMMAND0 { + address 0x084 + access_mode RW + bit CACHETHEN 0x80 + bit DPARCKEN 0x40 + bit MPARCKEN 0x20 + bit EXTREQLCK 0x10 + bit INTSCBRAMSEL 0x08 + bit RAMPS 0x04 + bit USCBSIZE32 0x02 + bit CIOPARCKEN 0x01 +} + /* * On the aic78X0 chips, Board Control is replaced by the DSCommand * register (p. 4-64) @@ -676,6 +702,7 @@ register ERROR { address 0x092 access_mode RO + bit CIOPARERR 0x80 /* Ultra2 only */ bit PCIERRSTAT 0x40 /* PCI only */ bit MPARERR 0x20 /* PCI only */ bit DPARERR 0x10 /* PCI only */ @@ -701,6 +728,7 @@ register DFCNTRL { address 0x093 access_mode RW + bit PRELOADEN 0x80 /* aic7890 only */ bit WIDEODD 0x40 bit SCSIEN 0x20 bit SDMAEN 0x10 @@ -715,6 +743,7 @@ register DFSTATUS { address 0x094 access_mode RO + bit PRELOAD_AVAIL 0x80 bit DWORDEMP 0x20 bit MREQPEND 0x10 bit HDONE 0x08 @@ -777,6 +806,14 @@ } /* + * Special Function + */ +register SFUNCT { + address 0x09f + access_mode RW +} + +/* * SCB Definition (p. 5-4) */ scb { @@ -865,6 +902,109 @@ register DSPCISTATUS { address 0x086 + mask DFTHRSH_100 0xc0 +} + +register CCHADDR { + address 0x0E0 + size 8 +} + +register CCHCNT { + address 0x0E8 +} + +register CCSGRAM { + address 0x0E9 +} + +register CCSGADDR { + address 0x0EA +} + +register CCSGCTL { + address 0x0EB + bit CCSGDONE 0x80 + bit CCSGEN 0x08 + bit FLAG 0x02 + bit CCSGRESET 0x01 +} + +register CCSCBCNT { + address 0xEF +} + +register CCSCBCTL { + address 0x0EE + bit CCSCBDONE 0x80 + bit ARRDONE 0x40 /* SCB Array prefetch done */ + bit CCARREN 0x10 + bit CCSCBEN 0x08 + bit CCSCBDIR 0x04 + bit CCSCBRESET 0x01 +} + +register CCSCBADDR { + address 0x0ED +} + +register CCSCBRAM { + address 0xEC +} + +register CCSCBPTR { + address 0x0F1 +} + +register HNSCB_QOFF { + address 0x0F4 +} + +register SNSCB_QOFF { + address 0x0F6 +} + +register SDSCB_QOFF { + address 0x0F8 +} + +register QOFF_CTLSTA { + address 0x0FA + bit SCB_AVAIL 0x40 + bit SNSCB_ROLLOVER 0x20 + bit SDSCB_ROLLOVER 0x10 + mask SCB_QSIZE 0x07 + mask SCB_QSIZE_256 0x06 +} + +register DFF_THRSH { + address 0x0FB + mask WR_DFTHRSH 0x70 + mask RD_DFTHRSH 0x07 + mask RD_DFTHRSH_MIN 0x00 + mask RD_DFTHRSH_25 0x01 + mask RD_DFTHRSH_50 0x02 + mask RD_DFTHRSH_63 0x03 + mask RD_DFTHRSH_75 0x04 + mask RD_DFTHRSH_85 0x05 + mask RD_DFTHRSH_90 0x06 + mask RD_DFTHRSH_MAX 0x07 + mask WR_DFTHRSH_MIN 0x00 + mask WR_DFTHRSH_25 0x10 + mask WR_DFTHRSH_50 0x20 + mask WR_DFTHRSH_63 0x30 + mask WR_DFTHRSH_75 0x40 + mask WR_DFTHRSH_85 0x50 + mask WR_DFTHRSH_90 0x60 + mask WR_DFTHRSH_MAX 0x70 +} + +register SG_CACHEPTR { + access_mode RW + address 0x0fc + mask SG_USER_DATA 0xfc + bit LAST_SEG 0x02 + bit LAST_SEG_DONE 0x01 } register BRDCTL { @@ -877,6 +1017,12 @@ bit BRDRW 0x04 bit BRDCTL1 0x02 bit BRDCTL0 0x01 + /* 7890 Definitions */ + bit BRDDAT4 0x10 + bit BRDDAT3 0x08 + bit BRDDAT2 0x04 + bit BRDRW_ULTRA2 0x02 + bit BRDSTB_ULTRA2 0x01 } /* @@ -935,7 +1081,7 @@ /* * 1 byte per target starting at this address for configuration values */ - TARG_SCRATCH { + TARG_SCSIRATE { size 16 } /* @@ -960,6 +1106,7 @@ /* Parameters for DMA Logic */ DMAPARAMS { size 1 + bit PRELOADEN 0x80 bit WIDEODD 0x40 bit SCSIEN 0x20 bit SDMAEN 0x10 @@ -1078,12 +1225,27 @@ mask MSGOUT_PHASEMIS 0x10 alias RETURN_1 } + ARG_2 { + size 1 + alias RETURN_2 + } + /* * Snapshot of MSG_OUT taken after each message is sent. */ LAST_MSG { size 1 } + + /* + * Number of times we have filled the CCSGRAM with prefetched + * SG elements. + */ + PREFETCH_CNT { + size 1 + } + + /* * These are reserved registers in the card's scratch ram. Some of * the values are specified in the AHA2742 technical reference manual @@ -1108,22 +1270,35 @@ mask BIOSDISABLED 0x30 bit CHANNEL_B_PRIMARY 0x08 } + /* + * Per target SCSI offset values for Ultra2 controllers. + */ + TARG_OFFSET { + address 0x070 + size 16 + } } const SCB_LIST_NULL 0xff +const CCSGADDR_MAX 0x80 +const CCSGRAM_MAXSEGS 16 + /* Offsets into the SCBID array where different data is stored */ const UNTAGGEDSCB_OFFSET 0 const QOUTFIFO_OFFSET 1 const QINFIFO_OFFSET 2 /* WDTR Message values */ -const BUS_8_BIT 0x00 +const BUS_8_BIT 0x00 const BUS_16_BIT 0x01 const BUS_32_BIT 0x02 + +/* Offset maximums */ const MAX_OFFSET_8BIT 0x0f -const MAX_OFFSET_16BIT 0x08 -const HOST_MSG 0xFF +const MAX_OFFSET_16BIT 0x08 +const MAX_OFFSET_ULTRA2 0x7f +const HOST_MSG 0xff /* Target mode command processing constants */ const CMD_GROUP_CODE_SHIFT 0x05 @@ -1135,11 +1310,7 @@ /* * Downloaded (kernel inserted) constants */ -/* - * Mask of bits to test against when looking at the Queue Count - * registers. Works around a bug on aic7850 chips. - */ -const QCNTMASK download + /* * Number of command descriptors in the command descriptor array. */ diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq --- v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.seq Mon Jul 13 13:46:34 1998 +++ linux/drivers/scsi/aic7xxx/aic7xxx.seq Sun Nov 15 10:33:07 1998 @@ -1,7 +1,7 @@ /* * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD. * - * Copyright (c) 1994-1997 Justin Gibbs. + * Copyright (c) 1994-1998 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -14,7 +14,7 @@ * derived from this software without specific prior written permission. * * Where this Software is combined with software released under the terms of - * the GNU Public License ("GPL") and the terms of the GPL would require the + * the GNU Public License (GPL) and the terms of the GPL would require the * combined work to also be released under the terms of the GPL, the terms * and conditions of this License will apply in addition to those of the * GPL with the exception of any terms or conditions of this License that @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $ + * $Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $ */ #include "aic7xxx.reg" @@ -59,38 +59,55 @@ reset: clr SCSISIGO; /* De-assert BSY */ /* Always allow reselection */ -.if ( TARGET_MODE ) - mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP; -.else - mvi SCSISEQ, ENRSELI|ENAUTOATNP; -.endif + if ((p->flags & AHC_TARGETMODE) != 0) { + mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP; + } else { + mvi SCSISEQ, ENRSELI|ENAUTOATNP; + } + + if ((p->features & AHC_CMD_CHAN) != 0) { + /* Ensure that no DMA operations are in progress */ + clr CCSGCTL; + clr CCSCBCTL; + } + call clear_target_state; and SXFRCTL0, ~SPIOEN; poll_for_work: - mov A, QINPOS; + if ((p->features & AHC_QUEUE_REGS) == 0) { + mov A, QINPOS; + } poll_for_work_loop: - and SEQCTL, ~PAUSEDIS; - test SSTAT0, SELDO|SELDI jnz selection; - test SCSISEQ, ENSELO jnz poll_for_work; -.if ( TWIN_CHANNEL ) - /* - * Twin channel devices cannot handle things like SELTO - * interrupts on the "background" channel. So, if we - * are selecting, keep polling the current channel util - * either a selection or reselection occurs. - */ - xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ + if ((p->features & AHC_QUEUE_REGS) == 0) { + and SEQCTL, ~PAUSEDIS; + } test SSTAT0, SELDO|SELDI jnz selection; test SCSISEQ, ENSELO jnz poll_for_work; - xor SBLKCTL,SELBUSB; /* Toggle back */ -.endif + if ((p->features & AHC_TWIN) != 0) { + /* + * Twin channel devices cannot handle things like SELTO + * interrupts on the "background" channel. So, if we + * are selecting, keep polling the current channel util + * either a selection or reselection occurs. + */ + xor SBLKCTL,SELBUSB; /* Toggle to the other bus */ + test SSTAT0, SELDO|SELDI jnz selection; + test SCSISEQ, ENSELO jnz poll_for_work; + xor SBLKCTL,SELBUSB; /* Toggle back */ + } cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting; test_queue: /* Has the driver posted any work for us? */ - or SEQCTL, PAUSEDIS; - cmp KERNEL_QINPOS, A je poll_for_work_loop; - inc QINPOS; - and SEQCTL, ~PAUSEDIS; + if ((p->features & AHC_QUEUE_REGS) != 0) { + test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop; + mov NONE, SNSCB_QOFF; + inc QINPOS; + } else { + or SEQCTL, PAUSEDIS; + cmp KERNEL_QINPOS, A je poll_for_work_loop; + inc QINPOS; + and SEQCTL, ~PAUSEDIS; + } /* * We have at least one queued SCB now and we don't have any @@ -98,26 +115,24 @@ * any SCBs available for use, pull the tag from the QINFIFO * and get to work on it. */ -.if ( SCB_PAGING ) - mov ALLZEROS call get_free_or_disc_scb; -.endif + if ((p->flags & AHC_PAGESCBS) != 0) { + mov ALLZEROS call get_free_or_disc_scb; + } + dequeue_scb: add A, -1, QINPOS; - mvi QINFIFO_OFFSET call set_SCBID_host_addr_and_cnt; - mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + mvi QINFIFO_OFFSET call fetch_byte; - call dma_finish; - mov SINDEX, DFDAT; -.if !( SCB_PAGING ) - /* In the non-paging case, the SCBID == hardware SCB index */ - mov SCBPTR, SINDEX; -.endif + if ((p->flags & AHC_PAGESCBS) == 0) { + /* In the non-paging case, the SCBID == hardware SCB index */ + mov SCBPTR, RETURN_2; + } dma_queued_scb: /* * DMA the SCB from host ram into the current SCB location. */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; - call dma_scb; + mov RETURN_2 call dma_scb; start_scb: /* @@ -135,16 +150,22 @@ jmp poll_for_work; start_selection: -.if ( TWIN_CHANNEL ) - and SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */ - and A,SELBUSB,SCB_TCL; /* Get new channel bit */ - or SINDEX,A; - mov SBLKCTL,SINDEX; /* select channel */ -.endif + if ((p->features & AHC_TWIN) != 0) { + and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */ + and A,SELBUSB,SCB_TCL; /* Get new channel bit */ + or SINDEX,A; + mov SBLKCTL,SINDEX; /* select channel */ + } initialize_scsiid: - and A, TID, SCB_TCL; /* Get target ID */ - and SCSIID, OID; /* Clear old target */ - or SCSIID, A; + if ((p->features & AHC_ULTRA2) != 0) { + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID_ULTRA2, OID; /* Clear old target */ + or SCSIID_ULTRA2, A; + } else { + and A, TID, SCB_TCL; /* Get target ID */ + and SCSIID, OID; /* Clear old target */ + or SCSIID, A; + } mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; /* @@ -155,143 +176,154 @@ initialize_channel: or A, CLRSTCNT|CLRCHN, SINDEX; or SXFRCTL0, A; -.if ( ULTRA ) + if ((p->features & AHC_ULTRA) != 0) { ultra: - mvi SINDEX, ULTRA_ENB+1; - test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */ - dec SINDEX; + mvi SINDEX, ULTRA_ENB+1; + test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */ + dec SINDEX; ultra_2: - mov FUNCTION1,SAVED_TCL; - mov A,FUNCTION1; - test SINDIR, A jz ndx_dtr; - or SXFRCTL0, FAST20; -.endif - + mov FUNCTION1,SAVED_TCL; + mov A,FUNCTION1; + test SINDIR, A jz ndx_dtr; + or SXFRCTL0, FAST20; + } /* * Initialize SCSIRATE with the appropriate value for this target. * The SCSIRATE settings for each target are stored in an array - * based at TARG_SCRATCH. + * based at TARG_SCSIRATE. */ ndx_dtr: shr A,4,SAVED_TCL; - test SBLKCTL,SELBUSB jz ndx_dtr_2; - or SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */ - or A,0x08; /* Channel B entries add 8 */ + if ((p->features & AHC_TWIN) != 0) { + test SBLKCTL,SELBUSB jz ndx_dtr_2; + or SAVED_TCL, SELBUSB; + or A,0x08; /* Channel B entries add 8 */ ndx_dtr_2: - add SINDEX,TARG_SCRATCH,A; + } + + if ((p->features & AHC_ULTRA2) != 0) { + add SINDEX, TARG_OFFSET, A; + mov SCSIOFFSET, SINDIR; + } + + add SINDEX,TARG_SCSIRATE,A; mov SCSIRATE,SINDIR ret; selection: test SSTAT0,SELDO jnz select_out; select_in: -.if ( TARGET_MODE ) - test SSTAT0, TARGET jz initiator_reselect; - /* - * We've just been selected. Assert BSY and - * setup the phase for receiving the messages - * from the target. - */ - mvi SCSISIGO, P_MESGOUT|BSYO; - mvi CLRSINT0, CLRSELDO; - - /* - * If ATN isn't asserted, go directly to bus free. - */ - test SCSISIGI, ATNI jz target_busfree; - - /* - * Setup the DMA for sending the identify and - * command information. - */ - mov A, TMODE_CMDADDR_NEXT; - mvi TMODE_CMDADDR call set_32byte_haddr_and_clrcnt; - mvi DFCNTRL, FIFORESET; + if ((p->flags & AHC_TARGETMODE) != 0) { + test SSTAT0, TARGET jz initiator_reselect; + /* + * We've just been selected. Assert BSY and + * setup the phase for receiving the messages + * from the target. + */ + mvi SCSISIGO, P_MESGOUT|BSYO; + mvi CLRSINT0, CLRSELDO; + + /* + * If ATN isn't asserted, go directly to bus free. + */ + test SCSISIGI, ATNI jz target_busfree; + + /* + * Setup the DMA for sending the identify and + * command information. + */ + mov A, TMODE_CMDADDR_NEXT; + mvi DINDEX, HADDR; + mvi TMODE_CMDADDR call set_32byte_addr; + mvi DFCNTRL, FIFORESET; - clr SINDEX; - /* Watch ATN closely now */ + clr SINDEX; + /* Watch ATN closely now */ message_loop: - or SXFRCTL0, SPIOEN; - test SSTAT0, SPIORDY jz .; - and SXFRCTL0, ~SPIOEN; - mov DINDEX, SCSIDATL; - mov DFDAT, DINDEX; - inc SINDEX; - - /* Message Testing... */ - test DINDEX, MSG_IDENTIFYFLAG jz . + 2; - mov ARG_1, DINDEX; - - test SCSISIGI, ATNI jnz message_loop; - add A, -4, SINDEX; - jc target_cmdphase; - mvi DFDAT, SCB_LIST_NULL; /* Terminate the message list */ + or SXFRCTL0, SPIOEN; + test SSTAT0, SPIORDY jz .; + and SXFRCTL0, ~SPIOEN; + mov DINDEX, SCSIDATL; + mov DFDAT, DINDEX; + inc SINDEX; + + /* Message Testing... */ + test DINDEX, MSG_IDENTIFYFLAG jz . + 2; + mov ARG_1, DINDEX; + + test SCSISIGI, ATNI jnz message_loop; + add A, -4, SINDEX; + jc target_cmdphase; + mvi DFDAT, SCB_LIST_NULL; /* Terminate the message list */ target_cmdphase: - add HCNT[0], 1, A; - mvi SCSISIGO, P_COMMAND|BSYO; - or SXFRCTL0, SPIOEN; - test SSTAT0, SPIORDY jz .; - mov A, SCSIDATL; - mov DFDAT, A; /* Store for host */ - - /* - * Determine the number of bytes to read - * based on the command group code. Count is - * one less than the total since we've already - * fetched the first byte. - */ - clr SINDEX; - shr A, CMD_GROUP_CODE_SHIFT; - add SEQADDR0, A; - - add SINDEX, CMD_GROUP0_BYTE_DELTA; - nop; /* Group 1 and 2 are the same */ - add SINDEX, CMD_GROUP2_BYTE_DELTA; - nop; /* Group 3 is reserved */ - add SINDEX, CMD_GROUP4_BYTE_DELTA; - add SINDEX, CMD_GROUP5_BYTE_DELTA; + add HCNT[0], 1, A; + clr HCNT[1]; + clr HCNT[2]; + mvi SCSISIGO, P_COMMAND|BSYO; + or SXFRCTL0, SPIOEN; + test SSTAT0, SPIORDY jz .; + mov A, SCSIDATL; + mov DFDAT, A; /* Store for host */ + + /* + * Determine the number of bytes to read + * based on the command group code. Count is + * one less than the total since we've already + * fetched the first byte. + */ + clr SINDEX; + shr A, CMD_GROUP_CODE_SHIFT; + add SEQADDR0, A; + + add SINDEX, CMD_GROUP0_BYTE_DELTA; + nop; /* Group 1 and 2 are the same */ + add SINDEX, CMD_GROUP2_BYTE_DELTA; + nop; /* Group 3 is reserved */ + add SINDEX, CMD_GROUP4_BYTE_DELTA; + add SINDEX, CMD_GROUP5_BYTE_DELTA; /* Group 6 and 7 are not handled yet */ - mov A, SINDEX; - add HCNT[0], A; + mov A, SINDEX; + add HCNT[0], A; command_loop: - test SSTAT0, SPIORDY jz .; - cmp SINDEX, 1 jne . + 2; - and SXFRCTL0, ~SPIOEN; /* Last Byte */ - mov DFDAT, SCSIDATL; - dec SINDEX; - test SINDEX, 0xFF jnz command_loop; + test SSTAT0, SPIORDY jz .; + cmp SINDEX, 1 jne . + 2; + and SXFRCTL0, ~SPIOEN; /* Last Byte */ + mov DFDAT, SCSIDATL; + dec SINDEX; + test SINDEX, 0xFF jnz command_loop; - or DFCNTRL, HDMAEN|FIFOFLUSH; + or DFCNTRL, HDMAEN|FIFOFLUSH; - call dma_finish; + call dma_finish; - test ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post; + test ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post; - mvi SCSISIGO, P_MESGIN|BSYO; + mvi SCSISIGO, P_MESGIN|BSYO; - or SXFRCTL0, SPIOEN; + or SXFRCTL0, SPIOEN; - mvi MSG_DISCONNECT call target_outb; + mvi MSG_DISCONNECT call target_outb; selectin_post: - inc TMODE_CMDADDR_NEXT; - cmp TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2; - clr TMODE_CMDADDR_NEXT; - mvi QOUTFIFO, SCB_LIST_NULL; - mvi INTSTAT,CMDCMPLT; + inc TMODE_CMDADDR_NEXT; + cmp TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2; + clr TMODE_CMDADDR_NEXT; + mvi QOUTFIFO, SCB_LIST_NULL; + mvi INTSTAT,CMDCMPLT; - test ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree; + test ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree; - /* Busy loop on something then go to data or status phase */ + /* Busy loop on something then go to data or status phase */ target_busfree: - clr SCSISIGO; - jmp poll_for_work; + clr SCSISIGO; + jmp poll_for_work; + + } -.endif /* TARGET_MODE */ /* * Reselection has been initiated by a target. Make a note that we've been * reselected, but haven't seen an IDENTIFY message from the target yet. @@ -396,26 +428,41 @@ jmp data_phase_loop; p_data: - mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; + if ((p->features & AHC_ULTRA2) != 0) { + mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN; + } else { + mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET; + } test LASTPHASE, IOI jnz . + 2; or DMAPARAMS, DIRECTION; call assert; /* * Ensure entering a data * phase is okay - seen identify, etc. */ - + if ((p->features & AHC_CMD_CHAN) != 0) { + mvi CCSGADDR, CCSGADDR_MAX; + } test SEQ_FLAGS, DPHASE jnz data_phase_reinit; + /* We have seen a data phase */ + or SEQ_FLAGS, DPHASE; + /* * Initialize the DMA address and counter from the SCB. * Also set SG_COUNT and SG_NEXT in memory since we cannot * modify the values in the SCB itself until we see a * save data pointers message. */ - mvi DINDEX, HADDR; - mvi SCB_DATAPTR call bcopy_7; - - call set_stcnt_from_hcnt; + if ((p->features & AHC_CMD_CHAN) != 0) { + bmov HADDR, SCB_DATAPTR, 7; + } else { + mvi DINDEX, HADDR; + mvi SCB_DATAPTR call bcopy_7; + } + + if ((p->features & AHC_ULTRA2) == 0) { + call set_stcnt_from_hcnt; + } mov SG_COUNT,SCB_SGCOUNT; @@ -432,19 +479,38 @@ * had an overrun. */ or SXFRCTL1,BITBUCKET; - mvi HCNT[0], 0xff; - mvi HCNT[1], 0xff; - mvi HCNT[2], 0xff; - call set_stcnt_from_hcnt; and DMAPARAMS, ~(HDMAEN|SDMAEN); - + if ((p->features & AHC_ULTRA2) != 0) { + bmov HCNT, ALLONES, 3; + } else { + mvi STCNT[0], 0xFF; + mvi STCNT[1], 0xFF; + mvi STCNT[2], 0xFF; + } data_phase_inbounds: -/* If we are the last SG block, ensure wideodd is off. */ +/* If we are the last SG block, tell the hardware. */ cmp SG_COUNT,0x01 jne data_phase_wideodd; - and DMAPARAMS, ~WIDEODD; + if ((p->features & AHC_ULTRA2) != 0) { + or SG_CACHEPTR, LAST_SEG; + } else { + and DMAPARAMS, ~WIDEODD; + } data_phase_wideodd: - mov DMAPARAMS call dma; + if ((p->features & AHC_ULTRA2) != 0) { + mov SINDEX, ALLONES; + mov DFCNTRL, DMAPARAMS; + test SSTAT0, SDONE jnz .;/* Wait for preload to complete */ +data_phase_dma_loop: + test SSTAT0, SDONE jnz data_phase_dma_done; + test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */ +data_phase_dma_phasemis: + test SSTAT0,SDONE jnz . + 2; + mov SINDEX,ALLZEROS; /* Remeber the phasemiss */ + } else { + mov DMAPARAMS call dma; + } +data_phase_dma_done: /* Go tell the host about any overruns */ test SXFRCTL1,BITBUCKET jnz data_phase_overrun; @@ -458,11 +524,6 @@ dec SG_COUNT; /* one less segment to go */ test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */ - - clr A; /* add sizeof(struct scatter) */ - add SG_NEXT[0],SG_SIZEOF; - adc SG_NEXT[1],A; - /* * Load a struct scatter and set up the data address and length. * If the working value of the SG count is nonzero, then @@ -471,33 +532,71 @@ * This, like all DMA's, assumes little-endian host data storage. */ sg_load: - mvi DINDEX, HADDR; - mvi SG_NEXT call bcopy_4; - - mvi HCNT[0],SG_SIZEOF; - clr HCNT[1]; - clr HCNT[2]; - - or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; - - call dma_finish; + if ((p->features & AHC_CMD_CHAN) != 0) { + /* + * Do we have any prefetch left??? + */ + cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail; + + /* + * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes. + */ + add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT; + mvi A, CCSGADDR_MAX; + jc . + 2; + shl A, 3, SG_COUNT; + mov CCHCNT, A; + bmov CCHADDR, SG_NEXT, 4; + mvi CCSGCTL, CCSGEN|CCSGRESET; + test CCSGCTL, CCSGDONE jz .; + and CCSGCTL, ~CCSGEN; + test CCSGCTL, CCSGEN jnz .; + mvi CCSGCTL, CCSGRESET; +prefetched_segs_avail: + bmov HADDR, CCSGRAM, 8; + } else { + mvi DINDEX, HADDR; + mvi SG_NEXT call bcopy_4; + + mvi HCNT[0],SG_SIZEOF; + clr HCNT[1]; + clr HCNT[2]; + + or DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + + call dma_finish; + + /* + * Copy data from FIFO into SCB data pointer and data count. + * This assumes that the SG segments are of the form: + * struct ahc_dma_seg { + * u_int32_t addr; four bytes, little-endian order + * u_int32_t len; four bytes, little endian order + * }; + */ + mvi HADDR call dfdat_in_7; + } + + if ((p->features & AHC_ULTRA2) == 0) { + /* Load STCNT as well. It is a mirror of HCNT */ + call set_stcnt_from_hcnt; + } -/* - * Copy data from FIFO into SCB data pointer and data count. This assumes - * that the SG segments are of the form: - * - * struct ahc_dma_seg { - * u_int32_t addr; four bytes, little-endian order - * u_int32_t len; four bytes, little endian order - * }; - */ - mvi HADDR call dfdat_in_7; +/* Advance the SG pointer */ + clr A; /* add sizeof(struct scatter) */ + add SG_NEXT[0],SG_SIZEOF; + adc SG_NEXT[1],A; -/* Load STCNT as well. It is a mirror of HCNT */ - call set_stcnt_from_hcnt; test SSTAT1,PHASEMIS jz data_phase_loop; + /* Ensure the last seg is visable at the shaddow layer */ + if ((p->features & AHC_ULTRA2) != 0) { + or DFCNTRL, PRELOADEN; + } data_phase_finish: + if ((p->features & AHC_ULTRA2) != 0) { + call ultra2_dmafinish; + } /* * After a DMA finishes, save the SG and STCNT residuals back into the SCB * We use STCNT instead of HCNT, since it's a reflection of how many bytes @@ -508,12 +607,17 @@ mov SCB_RESID_DCNT[2],STCNT[2]; mov SCB_RESID_SGCNT, SG_COUNT; - /* We have seen a data phase */ - or SEQ_FLAGS, DPHASE; + if ((p->features & AHC_ULTRA2) != 0) { + or SXFRCTL0, CLRSTCNT|CLRCHN; + } jmp ITloop; data_phase_overrun: + if ((p->features & AHC_ULTRA2) != 0) { + call ultra2_dmafinish; + or SXFRCTL0, CLRSTCNT|CLRCHN; + } /* * Turn off BITBUCKET mode and notify the host */ @@ -521,6 +625,19 @@ mvi INTSTAT,DATA_OVERRUN; jmp ITloop; +ultra2_dmafinish: + if ((p->features & AHC_ULTRA2) != 0) { + test DFCNTRL, DIRECTION jnz ultra2_dmahalt; + and DFCNTRL, ~SCSIEN; + test DFCNTRL, SCSIEN jnz .; + or DFCNTRL, FIFOFLUSH; + test DFSTATUS, FIFOEMP jz . - 1; +ultra2_dmahalt: + and DFCNTRL, ~(SCSIEN|HDMAEN); + test DFCNTRL, HDMAEN jnz .; + ret; + } + /* * Command phase. Set up the DMA registers and let 'er rip. */ @@ -530,14 +647,26 @@ /* * Load HADDR and HCNT. */ - mvi DINDEX, HADDR; - mvi SCB_CMDPTR call bcopy_5; - clr HCNT[1]; - clr HCNT[2]; - - call set_stcnt_from_hcnt; - - mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma; + if ((p->features & AHC_ULTRA2) != 0) { + or SG_CACHEPTR, LAST_SEG; + } + + if ((p->features & AHC_CMD_CHAN) != 0) { + bmov HADDR, SCB_CMDPTR, 5; + bmov HCNT[1], ALLZEROS, 2; + } else { + mvi DINDEX, HADDR; + mvi SCB_CMDPTR call bcopy_5; + clr HCNT[1]; + clr HCNT[2]; + } + + if ((p->features & AHC_ULTRA2) == 0) { + call set_stcnt_from_hcnt; + mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma; + } else { + mvi (PRELOADEN|SCSIEN|HDMAEN|DIRECTION) call dma; + } jmp ITloop; /* @@ -562,7 +691,6 @@ * on an SCB that might not be for the current nexus. (For example, a * BDR message in responce to a bad reselection would leave us pointed to * an SCB that doesn't have anything to do with the current target). - * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag, * bus device reset). * @@ -574,11 +702,11 @@ mov SINDEX, MSG_OUT; cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host; p_mesgout_identify: -.if ( WIDE ) - and SINDEX,0xf,SCB_TCL; /* lun */ -.else - and SINDEX,0x7,SCB_TCL; /* lun */ -.endif + if ((p->features & AHC_WIDE) != 0) { + and SINDEX,0xf,SCB_TCL; /* lun */ + } else { + and SINDEX,0x7,SCB_TCL; /* lun */ + } and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */ or SINDEX,A; /* or in disconnect privledge */ or SINDEX,MSG_IDENTIFYFLAG; @@ -606,6 +734,7 @@ p_mesgout_from_host: cmp SINDEX, HOST_MSG jne p_mesgout_onebyte; mvi INTSTAT,AWAITING_MSG; + nop; /* * Did the host detect a phase change? */ @@ -692,6 +821,7 @@ check_status: test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */ mvi INTSTAT,BAD_STATUS; /* let driver know */ + nop; cmp RETURN_1, SEND_SENSE jne complete; /* This SCB becomes the next to execute as it will retrieve sense */ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET; @@ -710,21 +840,21 @@ /* If we are untagged, clear our address up in host ram */ test SCB_CONTROL, TAG_ENB jnz complete_post; mov A, SAVED_TCL; - mvi UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt; - mvi DFCNTRL, FIFORESET; - mvi DFDAT, SCB_LIST_NULL; - or DFCNTRL, HDMAEN|FIFOFLUSH; - call dma_finish; + mvi UNTAGGEDSCB_OFFSET call post_byte_setup; + mvi SCB_LIST_NULL call post_byte; complete_post: /* Post the SCB and issue an interrupt */ - mov A, QOUTPOS; - mvi QOUTFIFO_OFFSET call set_SCBID_host_addr_and_cnt; - mvi DFCNTRL, FIFORESET; - mov DFDAT, SCB_TAG; - or DFCNTRL, HDMAEN|FIFOFLUSH; - call dma_finish; - inc QOUTPOS; + if ((p->features & AHC_QUEUE_REGS) != 0) { + mov A, SDSCB_QOFF; + } else { + mov A, QOUTPOS; + } + mvi QOUTFIFO_OFFSET call post_byte_setup; + mov SCB_TAG call post_byte; + if ((p->features & AHC_QUEUE_REGS) == 0) { + inc QOUTPOS; + } mvi INTSTAT,CMDCMPLT; add_to_free_list: @@ -796,18 +926,19 @@ * clearing the "disconnected" bit so we don't "find" it by accident later. */ mesgin_identify: -.if ( WIDE ) - and A,0x0f; /* lun in lower four bits */ -.else - and A,0x07; /* lun in lower three bits */ -.endif + + if ((p->features & AHC_WIDE) != 0) { + and A,0x0f; /* lun in lower four bits */ + } else { + and A,0x07; /* lun in lower three bits */ + } or SAVED_TCL,A; /* SAVED_TCL should be complete now */ call get_untagged_SCBID; cmp ARG_1, SCB_LIST_NULL je snoop_tag; -.if ( SCB_PAGING ) - test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB; -.endif + if ((p->flags & AHC_PAGESCBS) != 0) { + test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB; + } /* * If the SCB was found in the disconnected list (as is * always the case in non-paging scenarios), SCBPTR is already @@ -833,19 +964,19 @@ get_tag: mvi ARG_1 call inb_next; /* tag value */ -.if ! ( SCB_PAGING ) + if ((p->flags & AHC_PAGESCBS) == 0) { index_by_tag: - mov SCBPTR,ARG_1; - test SCB_CONTROL,TAG_ENB jz not_found; - mov SCBPTR call rem_scb_from_disc_list; -.else -/* - * Ensure that the SCB the tag points to is for an SCB transaction - * to the reconnecting target. - */ + mov SCBPTR,ARG_1; + test SCB_CONTROL,TAG_ENB jz not_found; + mov SCBPTR call rem_scb_from_disc_list; + } else { + /* + * Ensure that the SCB the tag points to is for + * an SCB transaction to the reconnecting target. + */ use_retrieveSCB: - call retrieveSCB; -.endif + call retrieveSCB; + } setup_SCB: mov A, SAVED_TCL; cmp SCB_TCL, A jne not_found_cleanup_scb; @@ -924,16 +1055,16 @@ inb_last: mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ -.if ( TARGET_MODE ) -/* - * Send a byte to an initiator in Automatic PIO mode. - * SPIOEN must be on prior to calling this routine. - */ +if ((p->flags & AHC_TARGETMODE) != 0) { + /* + * Send a byte to an initiator in Automatic PIO mode. + * SPIOEN must be on prior to calling this routine. + */ target_outb: mov SCSIDATL, SINDEX; test SSTAT0, SPIORDY jz .; ret; -.endif +} mesgin_phasemis: /* @@ -980,7 +1111,17 @@ dma_dmadone: and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); dma_halt: - test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt; + /* + * Some revisions of the aic7880 have a problem where, if the + * data fifo is full, but the PCI input latch is not empty, + * HDMAEN cannot be cleared. The fix used here is to attempt + * to drain the data fifo until there is space for the input + * latch to drain and HDMAEN de-asserts. + */ + if ((p->features & AHC_ULTRA2) == 0) { + mov NONE, DFDAT; + } + test DFCNTRL, HDMAEN jnz dma_halt; return: ret; @@ -1079,18 +1220,67 @@ mov ARG_1, SCB_TAG ret; mvi ARG_1, SCB_LIST_NULL ret; -set_SCBID_host_addr_and_cnt: - mov DINDEX, SINDEX; - mvi SCBID_ADDR call set_1byte_haddr_and_clrcnt; - mvi HCNT[0], 1 ret; +/* + * Fetch a byte from host memory given an index of (A + (256 * SINDEX)) + * and a base address of SCBID_ADDR. The byte is returned in RETURN_2. + */ +fetch_byte: + mov ARG_2, SINDEX; + if ((p->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi CCHCNT, 1; + mvi CCSGCTL, CCSGEN|CCSGRESET; + test CCSGCTL, CCSGDONE jz .; + mvi CCSGCTL, CCSGRESET; + bmov RETURN_2, CCSGRAM, 1 ret; + } else { + mvi DINDEX, HADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi HCNT[0], 1; + clr HCNT[1]; + clr HCNT[2]; + mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; + call dma_finish; + mov RETURN_2, DFDAT ret; + } + +/* + * Prepare the hardware to post a byte to host memory given an + * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR. + */ +post_byte_setup: + mov ARG_2, SINDEX; + if ((p->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi CCHCNT, 1; + mvi CCSCBCTL, CCSCBRESET ret; + } else { + mvi DINDEX, HADDR; + mvi SCBID_ADDR call set_1byte_addr; + mvi HCNT[0], 1; + clr HCNT[1]; + clr HCNT[2]; + mvi DFCNTRL, FIFORESET ret; + } + +post_byte: + if ((p->features & AHC_CMD_CHAN) != 0) { + bmov CCSCBRAM, SINDEX, 1; + or CCSCBCTL, CCSCBEN|CCSCBRESET; + test CCSCBCTL, CCSCBDONE jz .; + clr CCSCBCTL ret; + } else { + mov DFDAT, SINDEX; + or DFCNTRL, HDMAEN|FIFOFLUSH; + jmp dma_finish; + } get_SCBID_from_host: mov A, SAVED_TCL; - mvi UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt; - mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET; - - call dma_finish; - mov ARG_1, DFDAT ret; + mvi UNTAGGEDSCB_OFFSET call fetch_byte; + mov RETURN_1, RETURN_2 ret; phase_lock: test SSTAT1, REQINIT jz phase_lock; @@ -1116,65 +1306,102 @@ mov DINDIR, SINDIR ret; /* - * Setup haddr and count assuming that A is an - * index into an array of 32byte objects. + * Setup addr assuming that A is an index into + * an array of 32byte objects, SINDEX contains + * the base address of that array, and DINDEX + * contains the base address of the location + * to store the indexed address. */ -set_32byte_haddr_and_clrcnt: - shr DINDEX, 3, A; +set_32byte_addr: + shr ARG_2, 3, A; shl A, 5; -set_1byte_haddr_and_clrcnt: /* DINDEX must be 0 upon call */ - add HADDR[0], A, SINDIR; - mov A, DINDEX; - adc HADDR[1], A, SINDIR; +/* + * Setup addr assuming that A + (ARG_1 * 256) is an + * index into an array of 1byte objects, SINDEX contains + * the base address of that array, and DINDEX contains + * the base address of the location to store the computed + * address. + */ +set_1byte_addr: + add DINDIR, A, SINDIR; + mov A, ARG_2; + adc DINDIR, A, SINDIR; clr A; - adc HADDR[2], A, SINDIR; - adc HADDR[3], A, SINDIR; - /* Clear Count */ - clr HCNT[1]; - clr HCNT[2] ret; + adc DINDIR, A, SINDIR; + adc DINDIR, A, SINDIR ret; +/* + * Either post or fetch and SCB from host memory based on the + * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX. + */ dma_scb: - /* - * SCB index is in SINDEX. Determine the physical address in - * the host where this SCB is located and load HADDR with it. - */ mov A, SINDEX; - mvi HSCB_ADDR call set_32byte_haddr_and_clrcnt; - mvi HCNT[0], 28; - mov DFCNTRL, DMAPARAMS; - test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; - /* Fill it with the SCB data */ + if ((p->features & AHC_CMD_CHAN) != 0) { + mvi DINDEX, CCHADDR; + mvi HSCB_ADDR call set_32byte_addr; + mov CCSCBPTR, SCBPTR; + mvi CCHCNT, 32; + test DMAPARAMS, DIRECTION jz dma_scb_tohost; + mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET; + cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .; + jmp dma_scb_finish; +dma_scb_tohost: + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { + mvi CCSCBCTL, CCSCBRESET; + bmov CCSCBRAM, SCB_CONTROL, 32; + or CCSCBCTL, CCSCBEN|CCSCBRESET; + test CCSCBCTL, CCSCBDONE jz .; + } else { + mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET; + cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .; + } +dma_scb_finish: + clr CCSCBCTL; + test CCSCBCTL, CCARREN|CCSCBEN jnz .; + ret; + } else { + mvi DINDEX, HADDR; + mvi HSCB_ADDR call set_32byte_addr; + mvi HCNT[0], 32; + clr HCNT[1]; + clr HCNT[2]; + mov DFCNTRL, DMAPARAMS; + test DMAPARAMS, DIRECTION jnz dma_scb_fromhost; + /* Fill it with the SCB data */ copy_scb_tofifo: - mvi SINDEX, SCB_CONTROL; - add A, 28, SINDEX; + mvi SINDEX, SCB_CONTROL; + add A, 32, SINDEX; copy_scb_tofifo_loop: - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - mov DFDAT,SINDIR; - cmp SINDEX, A jne copy_scb_tofifo_loop; - or DFCNTRL, HDMAEN|FIFOFLUSH; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + mov DFDAT,SINDIR; + cmp SINDEX, A jne copy_scb_tofifo_loop; + or DFCNTRL, HDMAEN|FIFOFLUSH; dma_scb_fromhost: - call dma_finish; - /* If we were putting the SCB, we are done */ - test DMAPARAMS, DIRECTION jz return; - mvi SCB_CONTROL call dfdat_in_7; - call dfdat_in_7_continued; - call dfdat_in_7_continued; - jmp dfdat_in_7_continued; + call dma_finish; + /* If we were putting the SCB, we are done */ + test DMAPARAMS, DIRECTION jz return; + mvi SCB_CONTROL call dfdat_in_7; + call dfdat_in_7_continued; + call dfdat_in_7_continued; + jmp dfdat_in_7_continued; dfdat_in_7: - mov DINDEX,SINDEX; + mov DINDEX,SINDEX; dfdat_in_7_continued: - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT; - mov DINDIR,DFDAT ret; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT; + mov DINDIR,DFDAT ret; + } + /* * Wait for DMA from host memory to data FIFO to complete, then disable @@ -1188,13 +1415,13 @@ ret; add_scb_to_free_list: -.if ( SCB_PAGING ) - mov SCB_NEXT, FREE_SCBH; - mov FREE_SCBH, SCBPTR; -.endif + if ((p->flags & AHC_PAGESCBS) != 0) { + mov SCB_NEXT, FREE_SCBH; + mov FREE_SCBH, SCBPTR; + } mvi SCB_TAG, SCB_LIST_NULL ret; -.if ( SCB_PAGING ) +if ((p->flags & AHC_PAGESCBS) != 0) { get_free_or_disc_scb: cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb; cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb; @@ -1211,7 +1438,7 @@ dequeue_free_scb: mov SCBPTR, FREE_SCBH; mov FREE_SCBH, SCB_NEXT ret; -.endif +} add_scb_to_disc_list: /* diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h --- v2.0.35/linux/drivers/scsi/aic7xxx/sequencer.h Mon Jul 13 13:46:34 1998 +++ linux/drivers/scsi/aic7xxx/sequencer.h Sun Nov 15 10:33:07 1998 @@ -2,7 +2,7 @@ * Instruction formats for the sequencer program downloaded to * Aic7xxx SCSI host adapters * - * Copyright (c) 1997 Justin T. Gibbs. + * Copyright (c) 1997, 1998 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,43 +36,81 @@ * $Id: sequencer.h,v 1.3 1997/09/27 19:37:31 gibbs Exp $ */ +#ifdef __LITTLE_ENDIAN_BITFIELD struct ins_format1 { - unsigned char immediate; - unsigned char source; - unsigned char destination; - unsigned char opcode_ret; -#define DOWNLOAD_CONST_IMMEDIATE 0x80 + unsigned int + immediate : 8, + source : 9, + destination : 9, + ret : 1, + opcode : 4, + parity : 1; }; struct ins_format2 { - unsigned char shift_control; - unsigned char source; - unsigned char destination; - unsigned char opcode_ret; -#define RETURN_BIT 0x01 + unsigned int + shift_control : 8, + source : 9, + destination : 9, + ret : 1, + opcode : 4, + parity : 1; }; struct ins_format3 { - unsigned char immediate; - unsigned char source; - unsigned char address; - unsigned char opcode_addr; -#define ADDR_HIGH_BIT 0x01 + unsigned int + immediate : 8, + source : 9, + address : 10, + opcode : 4, + parity : 1; +}; +#elif defined(__BIG_ENDIAN_BITFIELD) +struct ins_format1 { + unsigned int + parity : 1, + opcode : 4, + ret : 1, + destination : 9, + source : 9, + immediate : 8; }; -#ifndef __KERNEL__ -struct instruction { - union { +struct ins_format2 { + unsigned int + parity : 1, + opcode : 4, + ret : 1, + destination : 9, + source : 9, + shift_control : 8; +}; + +struct ins_format3 { + unsigned int + parity : 1, + opcode : 4, + address : 10, + source : 9, + immediate : 8; +}; +#endif + +union ins_formats { struct ins_format1 format1; struct ins_format2 format2; struct ins_format3 format3; unsigned char bytes[4]; - } format; - u_int srcline; + unsigned int integer; +}; +struct instruction { + union ins_formats format; + unsigned int srcline; struct symbol *patch_label; - STAILQ_ENTRY(instruction) links; + struct { + struct instruction *stqe_next; + } links; }; -#endif #define AIC_OP_OR 0x0 #define AIC_OP_AND 0x1 @@ -80,6 +118,7 @@ #define AIC_OP_ADD 0x3 #define AIC_OP_ADC 0x4 #define AIC_OP_ROL 0x5 +#define AIC_OP_BMOV 0x6 #define AIC_OP_JMP 0x8 #define AIC_OP_JC 0x9 diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- v2.0.35/linux/drivers/scsi/aic7xxx.c Sun Nov 15 10:49:44 1998 +++ linux/drivers/scsi/aic7xxx.c Sun Nov 15 10:33:08 1998 @@ -98,7 +98,7 @@ /*+M************************************************************************** * - * Further driver modifications made by Doug Ledford + * Further driver modifications made by Doug Ledford * * Copyright (c) 1997-1998 Doug Ledford * @@ -163,13 +163,159 @@ * *_M*************************************************************************/ -#ifdef MODULE +/* + * The next three defines are user configurable. These should be the only + * defines a user might need to get in here and change. There are other + * defines buried deeper in the code, but those really shouldn't need touched + * under normal conditions. + */ + +/* + * AIC7XXX_FAKE_NEGOTIATION_CMDS + * We now have two distinctly different methods of device negotiation + * in this code. The two methods are selected by either defining or not + * defining this option. The difference is as follows: + * + * With AIC7XXX_FAKE_NEGOTIATION_CMDS not set (commented out) + * When the driver is in need of issuing a negotiation command for any + * given device, it will add the negotiation message on to part of a + * regular SCSI command for the device. In the process, if the device + * is configured for and using tagged queueing, then the code will + * also issue that single command as a non-tagged command, attach the + * negotiation message to that one command, and use a temporary + * queue depth of one to keep the untagged and tagged commands from + * overlapping. + * Pros: This doesn't use any extra SCB structures, it's simple, it + * works most of the time (if not all of the time now), and + * since we get the device capability info frmo the INQUIRY data + * now, shouldn't cause any problems. + * Cons: When we need to send a negotiation command to a device, we + * must use a command that is being sent to LUN 0 of the device. + * If we try sending one to high LUN numbers, then some devices + * get noticeably upset. Since we have to wait for a command with + * LUN == 0 to come along, we may not be able to renegotiate when + * we want if the user is actually using say LUN 1 of a CD Changer + * instead of using LUN 0 for an extended period of time. + * + * With AIC7XXX_FAKE_NEGOTIATION_CMDS defined + * When we need to negotiate with a device, instead of attaching our + * negotiation message to an existing command, we insert our own + * fictional Scsi_Cmnd into the chain that has the negotiation message + * attached to it. We send this one command as untagged regardless + * of the device type, and we fiddle with the queue depth the same as + * we would with the option unset to avoid overlapping commands. The + * primary difference between this and the unset option is that the + * negotiation message is no longer attached to a specific command, + * instead it is its own command and is merely triggered by a + * combination of both A) We need to negotiate and B) The mid level + * SCSI code has sent us a command. We still don't do any negotiation + * unless there is a valid SCSI command to be processed. + * Pros: This fixes the problem above in the Cons section. Since we + * issue our own fake command, we can set the LUN to 0 regardless + * of what the LUN is in the real command. It also means that if + * the device get's nasty over negotiation issues, it won't be + * showing up on a regular command, so we won't get any SENSE buffer + * data or STATUS_BYTE returns to the mid level code that are caused + * by snits in the negotiation code. + * Cons: We add more code, and more complexity. This means more ways + * in which things could break. It means a larger driver. It means + * more resource consumption for the fake commands. However, the + * biggest problem is this. Take a system where there is a CD-ROM + * on the SCSI bus. Someone has a CD in the CD-ROM and is using it. + * For some reason the SCSI bus gets reset. We don't touch the + * CD-ROM again for quite a period of time (so we don't renegotiate + * after the reset until we do touch the CD-ROM again). In the + * time while we aren't using the CD-ROM, the current disc is + * removed and a new one put in. When we go to check that disc, we + * will first have to renegotiate. In so doing, we issue our fake + * SCSI command, which happens to be TEST_UNIT_READY. The CD-ROM + * negotiates with us, then responds to our fake command with a + * CHECK_CONDITION status. We REQUEST_SENSE from the CD-ROM, it + * then sends the SENSE data to our fake command to tell it that + * it has been through a disc change. There, now we've cleared out + * the SENSE data along with our negotiation command, and when the + * real command executes, it won't pick up that the CD was changed. + * That's the biggest Con to this approach. In the future, I could + * probably code around this problem though, so this option is still + * viable. + * + * So, which command style should you use? I would appreciate it if people + * could try out both types. I want to know about any cases where one + * method works and the other doesn't. If one method works on significantly + * more systems than another, then it will become the default. If the second + * option turns out to work best, then I'll find a way to work around that + * big con I listed. + * + * -- July 7, 02:33 + * OK...I just added some code that should make the Con listed for the + * fake commands a non issue now. However, it needs testing. For now, + * I'm going to make the default to use the fake commands, we'll see how + * it goes. + */ + +#define AIC7XXX_FAKE_NEGOTIATION_CMDS + +/* + * AIC7XXX_STRICT_PCI_SETUP + * Should we assume the PCI config options on our controllers are set with + * sane and proper values, or should we be anal about our PCI config + * registers and force them to what we want? The main advantage to + * defining this option is on non-Intel hardware where the BIOS may not + * have been run to set things up, or if you have one of the BIOSless + * Adaptec controllers, such as a 2910, that don't get set up by the + * BIOS. However, keep in mind that we really do set the most important + * items in the driver regardless of this setting, this only controls some + * of the more esoteric PCI options on these cards. In that sense, I + * would default to leaving this off. However, if people wish to try + * things both ways, that would also help me to know if there are some + * machines where it works one way but not another. + * + * -- July 7, 17:09 + * OK...I need this on my machine for testing, so the default is to + * leave it defined. + * + * -- July 7, 18:49 + * I needed it for testing, but it didn't make any difference, so back + * off she goes. + * + * -- July 16, 23:04 + * I turned it back on to try and compensate for the 2.1.x PCI code + * which no longer relies solely on the BIOS and now tries to set + * things itself. + */ + +#define AIC7XXX_STRICT_PCI_SETUP + +/* + * AIC7XXX_VERBOSE_DEBUGGING + * This option enables a lot of extra printk();s in the code, surrounded + * by if (aic7xxx_verbose ...) statements. Executing all of those if + * statements and the extra checks can get to where it actually does have + * an impact on CPU usage and such, as well as code size. Disabling this + * define will keep some of those from becoming part of the code. + * + * NOTE: Currently, this option has no real effect, I will be adding the + * various #ifdef's in the code later when I've decided a section is + * complete and no longer needs debugging. OK...a lot of things are now + * surrounded by this define, so turning this off does have an impact. + */ + +/* + * #define AIC7XXX_VERBOSE_DEBUGGING + */ + +#if defined(MODULE) || defined(PCMCIA) #include #endif +#if defined(PCMCIA) +# undef MODULE +#endif + #include #include #include +#include #include #include #include @@ -190,7 +336,6 @@ #include "aic7xxx/sequencer.h" #include "aic7xxx/scsi_message.h" #include "aic7xxx_reg.h" -#include "aic7xxx_seq.h" #include #include /* for kmalloc() */ @@ -209,7 +354,7 @@ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "5.0.19" +#define AIC7XXX_C_VERSION "5.1.4" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -242,17 +387,15 @@ # include #endif -#if defined(__powerpc__) || defined(__i386__) -# define MMAPIO -#endif - #if defined(__powerpc__) +# define MMAPIO # ifdef mb # undef mb # endif # define mb() \ __asm__ __volatile__("eieio" ::: "memory") #elif defined(__i386__) +# define MMAPIO # ifdef mb # undef mb # endif @@ -330,7 +473,7 @@ * tagged queue value array is always active now. I've added * a setup option to set this particular array and I'm hoping * insmod will be smart enough to set it properly as well. It's - * by use of this array that a person can disable tagged queueing. + * by use of this array that a person can enable tagged queueing. * The DEFAULT_TAG_COMMANDS define has been changed to disable * tagged queueing by default, so if your devices can handle tagged * queueing you will need to add a line to their lilo.conf file like: @@ -396,6 +539,9 @@ */ /* + * NOTE: The below structure is for reference only, the actual structure + * to modify in order to change things is located around line + * number 1305 adapter_tag_info_t aic7xxx_tag_info[] = { {DEFAULT_TAG_COMMANDS}, @@ -429,7 +575,11 @@ "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */ "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */ - "Adaptec AIC-7895 Ultra SCSI host adapter" /* AIC_7895 */ + "Adaptec AIC-7895 Ultra SCSI host adapter", /* AIC_7895 */ + "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */ + "Adaptec AHA-294X Ultra2 SCSI host adapter", /* AIC_7890 */ + "Adaptec AIC-7896/7 Ultra2 SCSI host adapter", /* AIC_7896 */ + "Adaptec AHA-394X Ultra2 SCSI host adapter" /* AIC_7897 */ }; /* @@ -467,10 +617,10 @@ /* * Standard EISA Host ID regs (Offset from slot base) */ -#define HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */ -#define HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */ -#define HID2 0x82 /* product */ -#define HID3 0x83 /* firmware revision */ +#define AHC_HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */ +#define AHC_HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */ +#define AHC_HID2 0x82 /* product */ +#define AHC_HID3 0x83 /* firmware revision */ /* * AIC-7770 I/O range to reserve for a card @@ -494,11 +644,13 @@ #define LATTIME 0x0000FF00ul #define DEVCONFIG 0x40 -#define SCBSIZE32 0x00010400ul /* aic789X only */ +#define SCBSIZE32 0x00010000ul /* aic789X only */ #define MPORTMODE 0x00000400ul /* aic7870 only */ #define RAMPSM 0x00000200ul /* aic7870 only */ +#define RAMPSM_ULTRA2 0x00000004 #define VOLSENSE 0x00000100ul #define SCBRAMSEL 0x00000080ul +#define SCBRAMSEL_ULTRA2 0x00000008 #define MRDCEN 0x00000040ul #define EXTSCBTIME 0x00000020ul /* aic7870 only */ #define EXTSCBPEN 0x00000010ul /* aic7870 only */ @@ -507,6 +659,8 @@ #define STPWLEVEL 0x00000002ul #define DIFACTNEGEN 0x00000001ul /* aic7870 only */ +#define SCAMCTL 0x1a /* Ultra2 only */ +#define CCSCBBADDR 0xf0 /* aic7895/6/7 */ /* * Define the different types of SEEPROMs on aic7xxx adapters @@ -534,12 +688,14 @@ #define CFDISC 0x0010 /* enable disconnection */ #define CFWIDEB 0x0020 /* wide bus device (wide card) */ #define CFSYNCHISULTRA 0x0040 /* CFSYNC is an ultra offset */ -/* UNUSED 0x0080 */ +#define CFNEWULTRAFORMAT 0x0080 /* Use the Ultra2 SEEPROM format */ #define CFSTART 0x0100 /* send start unit SCSI command */ #define CFINCBIOS 0x0200 /* include in BIOS scan */ #define CFRNFOUND 0x0400 /* report even if not found */ #define CFMULTILUN 0x0800 /* probe mult luns in BIOS scan */ -/* UNUSED 0xF000 */ +#define CFWBCACHEYES 0x4000 /* Enable W-Behind Cache on drive */ +#define CFWBCACHENC 0xc000 /* Don't change W-Behind Cache */ +/* UNUSED 0x3000 */ unsigned short device_flags[16]; /* words 0-15 */ /* @@ -569,7 +725,9 @@ #define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */ #define CFRESETB 0x0040 /* reset SCSI bus at boot */ #define CFBPRIMARY 0x0100 /* Channel B primary on 7895 chipsets */ -/* UNUSED 0xFE80 */ +#define CFSEAUTOTERM 0x0400 /* aic7890 Perform SE Auto Term */ +#define CFLVDSTERM 0x0800 /* aic7890 LVD Termination */ +/* UNUSED 0xF280 */ unsigned short adapter_control; /* word 17 */ /* @@ -682,7 +840,6 @@ typedef enum { SCB_FREE = 0x0000, - SCB_WDTR_16BIT = 0x0001, SCB_WAITINGQ = 0x0002, SCB_ACTIVE = 0x0004, SCB_SENSE = 0x0008, @@ -691,11 +848,12 @@ SCB_RESET = 0x0040, SCB_RECOVERY_SCB = 0x0080, SCB_WAS_BUSY = 0x0100, + SCB_MSGOUT_SENT = 0x0200, SCB_MSGOUT_SDTR = 0x0400, SCB_MSGOUT_WDTR = 0x0800, - SCB_MSGOUT_WDTR_8BIT = 0x0800, - SCB_MSGOUT_WDTR_16BIT = 0x0801, - SCB_MSGOUT_BITS = SCB_MSGOUT_SDTR | SCB_MSGOUT_WDTR_16BIT, + SCB_MSGOUT_BITS = SCB_MSGOUT_SENT | + SCB_MSGOUT_SDTR | + SCB_MSGOUT_WDTR, SCB_QUEUED_ABORT = 0x1000, SCB_QUEUED_FOR_DONE = 0x2000 } scb_flag_type; @@ -711,7 +869,9 @@ AHC_EXTEND_TRANS_A = 0x00000100, AHC_EXTEND_TRANS_B = 0x00000200, AHC_TERM_ENB_A = 0x00000400, + AHC_TERM_ENB_SE_LOW = 0x00000400, AHC_TERM_ENB_B = 0x00000800, + AHC_TERM_ENB_SE_HIGH = 0x00000800, AHC_HANDLING_REQINITS = 0x00001000, AHC_TARGETMODE = 0x00002000, AHC_NEWEEPROM_FMT = 0x00004000, @@ -726,45 +886,62 @@ AHC_B_SCANNED = 0x00200000, AHC_MULTI_CHANNEL = 0x00400000, AHC_BIOS_ENABLED = 0x00800000, - AHC_ABORT_PENDING = 0x02000000, - AHC_RESET_PENDING = 0x04000000, + AHC_SEEPROM_FOUND = 0x01000000, + AHC_TERM_ENB_LVD = 0x02000000, + AHC_ABORT_PENDING = 0x04000000, + AHC_RESET_PENDING = 0x08000000, #define AHC_IN_ISR_BIT 28 AHC_IN_ISR = 0x10000000, AHC_IN_ABORT = 0x20000000, - AHC_IN_RESET = 0x40000000 + AHC_IN_RESET = 0x40000000, + AHC_EXTERNAL_SRAM = 0x80000000 } ahc_flag_type; typedef enum { - AHC_NONE = 0x00000000, - AHC_ULTRA = 0x00000001, - AHC_WIDE = 0x00000002, - AHC_TWIN = 0x00000008, - AHC_AIC7770 = 0x00000010, - AHC_AIC7850 = 0x00000020, - AHC_AIC7860 = 0x00000021, - AHC_AIC7870 = 0x00000040, - AHC_AIC7880 = 0x00000041, - AHC_AIC7895 = 0x00000081, - AHC_AIC78x0 = 0x000000E0, - AHC_274 = 0x00000110, - AHC_284 = 0x00000210, - AHC_294AU = 0x00000421, - AHC_294 = 0x00000440, - AHC_294U = 0x00000441, - AHC_394 = 0x00000840, - AHC_394U = 0x00000841, - AHC_394AU = 0x00000881, - AHC_398 = 0x00001040, - AHC_398U = 0x00001041, - AHC_39x = 0x00001880 -} ahc_type; + AHC_NONE = 0x0000, + AHC_CHIPID_MASK = 0x00ff, + AHC_AIC7770 = 0x0001, + AHC_AIC7850 = 0x0002, + AHC_AIC7860 = 0x0003, + AHC_AIC7870 = 0x0004, + AHC_AIC7880 = 0x0005, + AHC_AIC7890 = 0x0006, + AHC_AIC7895 = 0x0007, + AHC_AIC7896 = 0x0008, + AHC_VL = 0x0100, + AHC_EISA = 0x0200, + AHC_PCI = 0x0400, +} ahc_chip; + +typedef enum { + AHC_FENONE = 0x0000, + AHC_ULTRA = 0x0001, + AHC_ULTRA2 = 0x0002, + AHC_WIDE = 0x0004, + AHC_TWIN = 0x0008, + AHC_MORE_SRAM = 0x0010, + AHC_CMD_CHAN = 0x0020, + AHC_QUEUE_REGS = 0x0040, + AHC_SG_PRELOAD = 0x0080, + AHC_SPIOCAP = 0x0100, + AHC_AIC7770_FE = AHC_FENONE, + AHC_AIC7850_FE = AHC_SPIOCAP, + AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP, + AHC_AIC7870_FE = AHC_FENONE, + AHC_AIC7880_FE = AHC_ULTRA, + AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2| + AHC_QUEUE_REGS|AHC_SG_PRELOAD, + AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA, + AHC_AIC7896_FE = AHC_AIC7890_FE, +} ahc_feature; struct aic7xxx_scb { struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ struct aic7xxx_scb *q_next; /* next scb in queue */ - scb_flag_type flags; /* current state of scb */ + volatile scb_flag_type flags; /* current state of scb */ struct hw_scatterlist *sg_list; /* SG list in adapter format */ + void *kmalloc_ptr; unsigned char tag_action; unsigned char sg_count; unsigned char sense_cmd[6]; /* @@ -792,28 +969,50 @@ { ILLHADDR, "Illegal Host Access" }, { ILLSADDR, "Illegal Sequencer Address referenced" }, { ILLOPCODE, "Illegal Opcode in sequencer program" }, - { SQPARERR, "Sequencer Ram Parity Error" } + { SQPARERR, "Sequencer Ram Parity Error" }, + { DPARERR, "Data-Path Ram Parity Error" }, + { MPARERR, "Scratch Ram/SCB Array Ram Parity Error" }, + { PCIERRSTAT,"PCI Error detected" }, + { CIOPARERR, "CIOBUS Parity Error" } }; static unsigned char generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 }; typedef struct { - struct aic7xxx_hwscb *hscbs; scb_queue_type free_scbs; /* * SCBs assigned to free slot on * card (no paging required) */ + struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; + struct aic7xxx_hwscb *hscbs; unsigned char numscbs; /* current number of scbs */ unsigned char maxhscbs; /* hardware scbs */ unsigned char maxscbs; /* max scbs including pageable scbs */ - struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; + void *hscb_kmalloc_ptr; } scb_data_type; +struct target_cmd { + unsigned char mesg_bytes[4]; + unsigned char command[28]; +}; + +#define AHC_TRANS_CUR 0x0001 +#define AHC_TRANS_ACTIVE 0x0002 +#define AHC_TRANS_GOAL 0x0004 +#define AHC_TRANS_USER 0x0008 +#define AHC_TRANS_QUITE 0x0010 typedef struct { - unsigned char period; - unsigned char offset; -} syncinfo_type; + unsigned char cur_width; + unsigned char goal_width; + unsigned char cur_period; + unsigned char goal_period; + unsigned char cur_offset; + unsigned char goal_offset; + unsigned char user_width; + unsigned char user_period; + unsigned char user_offset; +} transinfo_type; /* * Define a structure used for each host adapter. Note, in order to avoid @@ -841,15 +1040,18 @@ spinlock_t spin_lock; #endif volatile unsigned char cpu_lock_count[NR_CPUS]; - ahc_type type; /* card type */ + ahc_chip chip; /* chip type */ + ahc_feature features; /* chip features */ unsigned long last_reset; unsigned long isr_count; /* Interrupt count */ unsigned long spurious_int; + struct target_cmd *targetcmds; + unsigned int num_targetcmds; unsigned short discenable; /* Targets allowed to disconnect */ unsigned short tagenable; /* Targets using tagged I/O */ unsigned short orderedtag; /* Ordered Q tags allowed */ volatile unsigned char activescbs; /* active scbs */ - unsigned char max_activescbs; + volatile unsigned char max_activescbs; unsigned char unpause; /* unpause value for HCNTRL */ unsigned char pause; /* pause value for HCNTRL */ volatile unsigned char qoutfifonext; @@ -870,7 +1072,7 @@ #define DEVICE_SCANNED 0x80 volatile unsigned char dev_flags[MAX_TARGETS]; volatile unsigned char dev_active_cmds[MAX_TARGETS]; - unsigned char dev_temp_queue_depth[MAX_TARGETS]; + volatile unsigned char dev_temp_queue_depth[MAX_TARGETS]; unsigned char dev_commands_sent[MAX_TARGETS]; /* @@ -917,7 +1119,7 @@ #define MSG_TYPE_INITIATOR_MSGIN 0x02 unsigned char msg_len; /* Length of message */ unsigned char msg_index; /* Index into msg_buf array */ - syncinfo_type syncinfo[MAX_TARGETS]; + transinfo_type transinfo[MAX_TARGETS]; volatile scb_queue_type waiting_scbs; /* * SCBs waiting for space in * the QINFIFO. @@ -945,10 +1147,10 @@ volatile unsigned char qoutfifo[256]; volatile unsigned char qinfifo[256]; unsigned int irq; /* IRQ for this adapter */ - unsigned short needsdtr; - unsigned short sdtr_pending; - unsigned short needwdtr; - unsigned short wdtr_pending; + volatile unsigned short needsdtr; + volatile unsigned short sdtr_pending; + volatile unsigned short needwdtr; + volatile unsigned short wdtr_pending; int instance; /* aic7xxx instance number */ int scsi_id; /* host adapter SCSI ID */ int scsi_id_b; /* channel B for twin adapters */ @@ -965,8 +1167,10 @@ #endif unsigned char pci_bus; unsigned char pci_device_fn; + struct seeprom_config sc; + unsigned short sc_type; + unsigned short sc_size; -#ifdef AIC7XXX_PROC_STATS /* * Statistics Kept: * @@ -980,18 +1184,21 @@ * * NOTE: Enabling this feature is likely to cause a noticeable performance * decrease as the accesses into the stats structures blows apart multiple - * cache lines and is CPU time consuming. + * cache lines and is CPU time consuming. We keep the xfer count always + * for use by the aic7xxx_proc.c code, but only do the bins if the + * proc stats code is enabled. */ struct aic7xxx_xferstats { long xfers; /* total xfer count */ long w_total; /* total writes */ long w_total512; /* 512 byte blocks written */ - long w_bins[10]; /* binned write */ long r_total; /* total reads */ long r_total512; /* 512 byte blocks read */ +#ifdef AIC7XXX_PROC_STATS + long w_bins[10]; /* binned write */ long r_bins[10]; /* binned reads */ - } stats[MAX_TARGETS][MAX_LUNS]; /* [(channel << 3)|target][lun] */ #endif /* AIC7XXX_PROC_STATS */ + } stats[MAX_TARGETS][MAX_LUNS]; /* [(channel << 3)|target][lun] */ }; /* @@ -999,29 +1206,33 @@ * Provides a mapping of transfer periods in ns/4 to the proper value to * stick in the SCSIRATE reg to use that transfer rate. */ -static struct { - short period; +#define AHC_SYNCRATE_ULTRA2 0 +#define AHC_SYNCRATE_ULTRA 2 +#define AHC_SYNCRATE_FAST 5 +static struct aic7xxx_syncrate { /* Rates in Ultra mode have bit 8 of sxfr set */ #define ULTRA_SXFR 0x100 - short rate; - const char *english; + int sxfr_ultra2; + int sxfr; + unsigned char period; + const char *rate[2]; } aic7xxx_syncrates[] = { - { 12, 0x100, "20.0" }, - { 15, 0x110, "16.0" }, - { 18, 0x120, "13.4" }, - { 25, 0x000, "10.0" }, - { 31, 0x010, "8.0" }, - { 37, 0x020, "6.67" }, - { 43, 0x030, "5.7" }, - { 50, 0x040, "5.0" }, - { 56, 0x050, "4.4" }, - { 62, 0x060, "4.0" }, - { 68, 0x070, "3.6" } + { 0x13, 0x000, 10, {"40.0", "80.0"} }, + { 0x14, 0x000, 11, {"33.0", "66.6"} }, + { 0x15, 0x100, 12, {"20.0", "40.0"} }, + { 0x16, 0x110, 15, {"16.0", "32.0"} }, + { 0x17, 0x120, 18, {"13.4", "26.8"} }, + { 0x18, 0x000, 25, {"10.0", "20.0"} }, + { 0x19, 0x010, 31, {"8.0", "16.0"} }, + { 0x1a, 0x020, 37, {"6.67", "13.3"} }, + { 0x1b, 0x030, 43, {"5.7", "11.4"} }, + { 0x10, 0x040, 50, {"5.0", "10.0"} }, + { 0x00, 0x050, 56, {"4.4", "8.8" } }, + { 0x00, 0x060, 62, {"4.0", "8.0" } }, + { 0x00, 0x070, 68, {"3.6", "7.2" } }, + { 0x00, 0x000, 0, {NULL, NULL} }, }; -static int num_aic7xxx_syncrates = - sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]); - #define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1), \ (((scb->hscb)->target_channel_lun >> 4) & 0xf), \ ((scb->hscb)->target_channel_lun & 0x07) @@ -1041,58 +1252,170 @@ /* * XXX - these options apply unilaterally to _all_ 274x/284x/294x - * cards in the system. This should be fixed. + * cards in the system. This should be fixed. Exceptions to this + * rule are noted in the comments. */ -static int aic7xxx_7895_irq_hack = -1; /* This enables a hack to fix - * IRQ settings on buggy 7895 - * MB controller setups - * -1 == Disable this hack - * 0 == Use the Channel A IRQ - * 1 == Use the Channel B IRQ - */ -static unsigned int aic7xxx_extended = 0; /* extended translation on? */ -static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */ -static int aic7xxx_irq_trigger = -1; /* - * -1 use board setting - * 0 use edge triggered - * 1 use level triggered - */ -static int aic7xxx_reverse_scan = 0; /* - * Set this to anything but 0 - * to make the probe code - * reverse the order of PCI - * devices - */ -static int aic7xxx_override_term = 0; /* - * Set this to non-0 to make the - * driver override any BIOS - * configured termination - * settings based upon the - * results of the cable detect - * logic. This only applies - * to cards that have cable - * detection logic and a SEEPROM - */ -static int aic7xxx_panic_on_abort = 0; /* - * Set this to non-0 in order - * to force the driver to panic - * the kernel and print out - * debugging info on an abort - * or reset call into the - * driver. - */ -static int aic7xxx_pci_parity = 0; /* - * Set this to: - * 0 - Shut off PCI parity check - * -1 - Normal parity check - * anything else - reverse pci - * pci parity checking - */ + + +/* + * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This + * has no effect on any later resets that might occur due to things like + * SCSI bus timeouts. + */ +static unsigned int aic7xxx_no_reset = 0; +/* + * Certain PCI motherboards will scan PCI devices from highest to lowest, + * others scan from lowest to highest, and they tend to do all kinds of + * strange things when they come into contact with PCI bridge chips. The + * net result of all this is that the PCI card that is actually used to boot + * the machine is very hard to detect. Most motherboards go from lowest + * PCI slot number to highest, and the first SCSI controller found is the + * one you boot from. The only exceptions to this are when a controller + * has its BIOS disabled. So, we by default sort all of our SCSI controllers + * from lowest PCI slot number to highest PCI slot number. We also force + * all controllers with their BIOS disabled to the end of the list. This + * works on *almost* all computers. Where it doesn't work, we have this + * option. Setting this option to non-0 will reverse the order of the sort + * to highest first, then lowest, but will still leave cards with their BIOS + * disabled at the very end. That should fix everyone up unless there are + * really strange cirumstances. + */ +static int aic7xxx_reverse_scan = 0; +/* + * This setting enables a hack to fix the IRQ settings on buggy 7895 + * MB controller setups: + * -1 == Disable this hack + * 0 == Use the Channel A IRQ for both channels + * 1 == Use the Channel B IRQ for both channels + */ +static unsigned int aic7xxx_extended = 0; +/* + * The IRQ trigger method used on EISA controllers. Does not effect PCI cards. + * -1 = Use detected settings. + * 0 = Force Edge triggered mode. + * 1 = Force Level triggered mode. + */ +static int aic7xxx_irq_trigger = -1; +/* + * This variable is used to override the termination settings on a controller. + * This should not be used under normal conditions. However, in the case + * that a controller does not have a readable SEEPROM (so that we can't + * read the SEEPROM settings directly) and that a controller has a buggered + * version of the cable detection logic, this can be used to force the + * correct termination. It is preferable to use the manual termination + * settings in the BIOS if possible, but some motherboard controllers store + * those settings in a format we can't read. In other cases, auto term + * should also work, but the chipset was put together with no auto term + * logic (common on motherboard controllers). In those cases, we have + * 32 bits here to work with. That's good for 8 controllers/channels. The + * bits are organized as 4 bits per channel, with scsi0 getting the lowest + * 4 bits in the int. A 1 in a bit position indicates the termination setting + * that corresponds to that bit should be enabled, a 0 is disabled. + * It looks something like this: + * + * 0x0f = 1111-Single Ended Low Byte Termination on/off + * ||\-Single Ended High Byte Termination on/off + * |\-LVD Low Byte Termination on/off + * \-LVD High Byte Termination on/off + * + * For non-Ultra2 controllers, the upper 2 bits are not important. So, to + * enable both high byte and low byte termination on scsi0, I would need to + * make sure that the override_term variable was set to 0x03 (bits 0011). + * To make sure that all termination is enabled on an Ultra2 controller at + * scsi2 and only high byte termination on scsi1 and high and low byte + * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011) + * + * For the most part, users should never have to use this, that's why I + * left it fairly cryptic instead of easy to understand. If you need it, + * most likely someone will be telling you what your's needs to be set to. + */ +static int aic7xxx_override_term = -1; +/* + * Certain motherboard chipset controllers tend to screw + * up the polarity of the term enable output pin. Use this variable + * to force the correct polarity for your system. This is a bitfield variable + * similar to the previous one, but this one has one bit per channel instead + * of four. + * 0 = Force the setting to active low. + * 1 = Force setting to active high. + * Most Adaptec cards are active high, several motherboards are active low. + * To force a 2940 card at SCSI 0 to active high and a motherboard 7895 + * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3 + * to active high, you would need to set stpwlev=0x9 (bits 1001). + * + * People shouldn't need to use this, but if you are experiencing lots of + * SCSI timeout problems, this may help. There is one sure way to test what + * this option needs to be. Using a boot floppy to boot the system, configure + * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and + * if needed then also pass a value to override_term to make sure that the + * driver is enabling SCSI termination, then set this variable to either 0 + * or 1. When the driver boots, make sure there are *NO* SCSI cables + * connected to your controller. If it finds and inits the controller + * without problem, then the setting you passed to stpwlev was correct. If + * the driver goes into a reset loop and hangs the system, then you need the + * other setting for this variable. If neither setting lets the machine + * boot then you have definite termination problems that may not be fixable. + */ +static int aic7xxx_stpwlev = -1; +/* + * Set this to non-0 in order to force the driver to panic the kernel + * and print out debugging info on a SCSI abort or reset cycle. + */ +static int aic7xxx_panic_on_abort = 0; +/* + * PCI bus parity checking of the Adaptec controllers. This is somewhat + * dubious at best. To my knowledge, this option has never actually + * solved a PCI parity problem, but on certain machines with broken PCI + * chipset configurations, it can generate tons of false error messages. + * It's included in the driver for completeness. + * 0 = Shut off PCI parity check + * -1 = Normal polarity pci parity checking + * 1 = reverse polarity pci parity checking + * + * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this + * variable to -1 you would actually want to simply pass the variable + * name without a number. That will invert the 0 which will result in + * -1. + */ +static int aic7xxx_pci_parity = 0; +/* + * Set this to any non-0 value to cause us to dump the contents of all + * the card's registers in a hex dump format tailored to each model of + * controller. + * + * NOTE: THE CONTROLLER IS LEFT IN AN UNUSEABLE STATE BY THIS OPTION. + * YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES + * ONLY + */ +static int aic7xxx_dump_card = 0; +/* + * Set this to a non-0 value to make us dump out the 32 bit instruction + * registers on the card after completing the sequencer download. This + * allows the actual sequencer download to be verified. It is possible + * to use this option and still boot up and run your system. This is + * only intended for debugging purposes. + */ +static int aic7xxx_dump_sequencer = 0; +/* + * Certain newer motherboards have put new PCI based devices into the + * IO spaces that used to typically be occupied by VLB or EISA cards. + * This overlap can cause these newer motherboards to lock up when scanned + * for older EISA and VLB devices. Setting this option to non-0 will + * cause the driver to skip scanning for any VLB or EISA controllers and + * only support the PCI controllers. NOTE: this means that if the kernel + * os compiled with PCI support disabled, then setting this to non-0 + * would result in never finding any devices :) + */ +static int aic7xxx_no_probe = 0; + /* * So that insmod can find the variable and make it point to something */ #ifdef MODULE static char * aic7xxx = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,18) +MODULE_PARM(aic7xxx, "s"); +#endif /* * Just in case someone uses commas to separate items on the insmod @@ -1138,9 +1461,9 @@ #define VERBOSE_SCSIINT 0x0004 #define VERBOSE_PROBE 0x0008 #define VERBOSE_PROBE2 0x0010 -#define VERBOSE_QUEUE 0x0020 +#define VERBOSE_NEGOTIATION2 0x0020 #define VERBOSE_MINOR_ERROR 0x0040 -#define VERBOSE_QUEUE_FULL 0x0080 +#define VERBOSE_TRACING 0x0080 #define VERBOSE_ABORT 0x0f00 #define VERBOSE_ABORT_MID 0x0100 #define VERBOSE_ABORT_FIND 0x0200 @@ -1157,49 +1480,75 @@ /**************************************************************************** * - * These functions are not used yet, but when we do memory mapped - * IO, we'll use them then. + * We're going to start putting in function declarations so that order of + * functions is no longer important. As needed, they are added here. + * + ***************************************************************************/ + +static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd); +static void aic7xxx_print_card(struct aic7xxx_host *p); +static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p); +static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded); +#ifdef AIC7XXX_VERBOSE_DEBUGGING +static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer); +#endif + +/**************************************************************************** + * + * These functions are now used. They happen to be wrapped in useless + * inb/outb port read/writes around the real reads and writes because it + * seems that certain very fast CPUs have a problem dealing with us when + * going at full speed. * ***************************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) +static inline void +mdelay(int milliseconds) +{ + int i; + + for(i=0; imaddr) + { x = p->maddr[port]; + } else + { x = inb(p->base + port); + } mb(); return(x); +#else + return(inb(p->base + port)); +#endif } static inline void aic_outb(struct aic7xxx_host *p, unsigned char val, long port) { +#ifdef MMAPIO if(p->maddr) + { p->maddr[port] = val; + } else - outb(val, p->base + port); - mb(); -} - -static inline void -aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size) -{ - if(p->maddr) { - int i; - - for (i=0; i < size; i++) - { - p->maddr[port] = valp[i]; - } + outb(val, p->base + port); } - else - outsb(p->base + port, valp, size); mb(); +#else + outb(val, p->base + port); +#endif } /*+F************************************************************************* @@ -1227,10 +1576,13 @@ { "irq_trigger", &aic7xxx_irq_trigger }, { "verbose", &aic7xxx_verbose }, { "reverse_scan",&aic7xxx_reverse_scan }, - { "7895_irq_hack", &aic7xxx_7895_irq_hack }, { "override_term", &aic7xxx_override_term }, + { "stpwlev", &aic7xxx_stpwlev }, + { "no_probe", &aic7xxx_no_probe }, { "panic_on_abort", &aic7xxx_panic_on_abort }, { "pci_parity", &aic7xxx_pci_parity }, + { "dump_card", &aic7xxx_dump_card }, + { "dump_sequencer", &aic7xxx_dump_sequencer }, { "tag_info", NULL } }; @@ -1327,7 +1679,7 @@ } else if (!strncmp(p, "verbose", n)) { - *(options[i].flag) = 0xff69; + *(options[i].flag) = 0xff09; } else { @@ -1387,54 +1739,66 @@ static inline void restart_sequencer(struct aic7xxx_host *p) { - /* Set the sequencer address to 0. */ aic_outb(p, 0, SEQADDR0); aic_outb(p, 0, SEQADDR1); - - /* - * Reset and unpause the sequencer. The reset is suppose to - * start the sequencer running, so we immediately do a pause_sequencer - * since some of our code expects the sequencer paused after a restart - */ - aic_outb(p, SEQRESET | FASTMODE, SEQCTL); - pause_sequencer(p); + aic_outb(p, FASTMODE, SEQCTL); } +/* + * We include the aic7xxx_seq.c file here so that the other defines have + * already been made, and so that it comes before the code that actually + * downloads the instructions (since we don't typically use function + * prototype, our code has to be ordered that way, it's a left-over from + * the original driver days.....I should fix it some time DL). + */ +#include "aic7xxx_seq.c" /*+F************************************************************************* * Function: - * aic7xxx_next_patch + * aic7xxx_check_patch * * Description: - * Find the next patch to download. + * See if the next patch to download should be downloaded. *-F*************************************************************************/ -static struct sequencer_patch * -aic7xxx_next_patch(struct sequencer_patch *cur_patch, int options, int instrptr) +static int +aic7xxx_check_patch(struct aic7xxx_host *p, + struct sequencer_patch **start_patch, int start_instr, int *skip_addr) { - while (cur_patch != NULL) + struct sequencer_patch *cur_patch; + struct sequencer_patch *last_patch; + int num_patches; + + num_patches = sizeof(sequencer_patches)/sizeof(struct sequencer_patch); + last_patch = &sequencer_patches[num_patches]; + cur_patch = *start_patch; + + while ((cur_patch < last_patch) && (start_instr == cur_patch->begin)) { - if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE)) - || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE)) - || (instrptr >= cur_patch->end)) + if (cur_patch->patch_func(p) == 0) { /* - * Either we want to keep this section of code, or we have consumed - * this patch. Skip to the next patch. + * Start rejecting code. */ - cur_patch++; - if (cur_patch->options == 0) - { - /* Out of patches. */ - cur_patch = NULL; - } + *skip_addr = start_instr + cur_patch->skip_instr; + cur_patch += cur_patch->skip_patch; } else { - /* Found an OK patch. */ - break; + /* + * Found an OK patch. Advance the patch pointer to the next patch + * and wait for our instruction pointer to get here. + */ + cur_patch++; } } - return (cur_patch); + + *start_patch = cur_patch; + if (start_instr < *skip_addr) + /* + * Still skipping + */ + return (0); + return(1); } @@ -1446,15 +1810,23 @@ * Find the next patch to download. *-F*************************************************************************/ static void -aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr) +aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr, + unsigned char *dconsts) { + union ins_formats instr; + struct ins_format1 *fmt1_ins; + struct ins_format3 *fmt3_ins; unsigned char opcode; - struct ins_format3 instr; - unsigned char dconsts[4] = { 0, 0, 0, 0 }; - instr = *(struct ins_format3 *) &seqprog[instrptr * 4]; + instr = *(union ins_formats*) &seqprog[instrptr * 4]; + + instr.integer = le32_to_cpu(instr.integer); + + fmt1_ins = &instr.format1; + fmt3_ins = NULL; + /* Pull the opcode */ - opcode = (instr.opcode_addr & ~DOWNLOAD_CONST_IMMEDIATE) >> 1; + opcode = instr.format1.opcode; switch (opcode) { case AIC_OP_JMP: @@ -1466,44 +1838,89 @@ case AIC_OP_JE: case AIC_OP_JZ: { + struct sequencer_patch *cur_patch; int address_offset; unsigned int address; - struct sequencer_patch *patch; + int skip_addr; int i; + fmt3_ins = &instr.format3; address_offset = 0; - address = instr.address; - address |= (instr.opcode_addr & ADDR_HIGH_BIT) << 8; - for (i = 0; i < NUMBER(sequencer_patches); i++) + address = fmt3_ins->address; + cur_patch = sequencer_patches; + skip_addr = 0; + + for (i = 0; i < address;) { - patch = &sequencer_patches[i]; - if ((((patch->options & options) == 0) && (patch->negative == FALSE)) || - (((patch->options & options) != 0) && (patch->negative == TRUE))) + aic7xxx_check_patch(p, &cur_patch, i, &skip_addr); + if (skip_addr > i) { - if (address >= patch->end) - { - address_offset += patch->end - patch->begin; - } + int end_addr; + + end_addr = MIN(address, skip_addr); + address_offset += end_addr - i; + i = skip_addr; + } + else + { + i++; } } address -= address_offset; - instr.address = address & 0xFF; - instr.opcode_addr &= ~ADDR_HIGH_BIT; - instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT; + fmt3_ins->address = address; + /* Fall Through to the next code section */ } - /* Fall through */ case AIC_OP_OR: case AIC_OP_AND: case AIC_OP_XOR: case AIC_OP_ADD: case AIC_OP_ADC: - if (instr.opcode_addr & DOWNLOAD_CONST_IMMEDIATE) + case AIC_OP_BMOV: + if (fmt1_ins->parity != 0) { - instr.immediate = dconsts[instr.immediate]; + fmt1_ins->immediate = dconsts[fmt1_ins->immediate]; } - instr.opcode_addr &= ~DOWNLOAD_CONST_IMMEDIATE; + fmt1_ins->parity = 0; + /* Fall Through to the next code section */ case AIC_OP_ROL: - aic_outsb(p, SEQRAM, &instr.immediate, 4); + if ((p->features & AHC_ULTRA2) != 0) + { + int i, count; + + /* Calculate odd parity for the instruction */ + for ( i=0, count=0; i < 31; i++) + { + unsigned int mask; + + mask = 0x01 << i; + if ((instr.integer & mask) != 0) + count++; + } + if (!(count & 0x01)) + instr.format1.parity = 1; + } + else + { + if (fmt3_ins != NULL) + { + instr.integer = fmt3_ins->immediate | + (fmt3_ins->source << 8) | + (fmt3_ins->address << 16) | + (fmt3_ins->opcode << 25); + } + else + { + instr.integer = fmt1_ins->immediate | + (fmt1_ins->source << 8) | + (fmt1_ins->destination << 16) | + (fmt1_ins->ret << 24) | + (fmt1_ins->opcode << 25); + } + } + aic_outb(p, (instr.integer & 0xff), SEQRAM); + aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM); + aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM); + aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM); break; default: @@ -1523,89 +1940,93 @@ static void aic7xxx_loadseq(struct aic7xxx_host *p) { - int options; struct sequencer_patch *cur_patch; int i; int downloaded; + int skip_addr; + unsigned char download_consts[4] = {0, 0, 0, 0}; if (aic7xxx_verbose & VERBOSE_PROBE) { printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no); } - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("\n"); - options = 1; /* Code for all options. */ + download_consts[TMODE_NUMCMDS] = p->num_targetcmds; + cur_patch = &sequencer_patches[0]; downloaded = 0; - if (p->type & AHC_ULTRA) - { - options |= ULTRA; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Will download code for option ULTRA\n", - p->host_no); - } - if (p->type & AHC_TWIN) - { - options |= TWIN_CHANNEL; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Will download code for option " - "TWIN_CHANNEL\n", p->host_no); - } - if (p->type & AHC_WIDE) - { - options |= WIDE; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Will download code for option WIDE\n", - p->host_no); - } - /* if (p->scb_data->maxscbs > p->scb_data->maxhscbs) this should always - be true, don't test, - just do. */ - { - options |= SCB_PAGING; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Will download code for option SCB_PAGING\n", - p->host_no); - } - /* We don't actually support target mode yet, so leave this out - if (p->flags & AHC_TARGETMODE) - options |= TARGET_MODE; */ - - if ( (options & ~(ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01)) ) - { - printk(KERN_INFO "(scsi%d) Unknown bits set in the options field, " - "correcting.\n", p->host_no); - options &= ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01; - } + skip_addr = 0; - - cur_patch = sequencer_patches; - aic_outb(p, PERRORDIS | LOADRAM, SEQCTL); + aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL); aic_outb(p, 0, SEQADDR0); aic_outb(p, 0, SEQADDR1); for (i = 0; i < sizeof(seqprog) / 4; i++) { - cur_patch = aic7xxx_next_patch(cur_patch, options, i); - if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i)) + if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0) { /* Skip this instruction for this configuration. */ continue; } - aic7xxx_download_instr(p, options, i); + aic7xxx_download_instr(p, i, &download_consts[0]); downloaded++; } - aic_outb(p, FASTMODE, SEQCTL); aic_outb(p, 0, SEQADDR0); aic_outb(p, 0, SEQADDR1); - - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) Download complete,", p->host_no); - + aic_outb(p, FASTMODE | FAILDIS, SEQCTL); + unpause_sequencer(p, TRUE); + mdelay(1); + pause_sequencer(p); + aic_outb(p, FASTMODE, SEQCTL); if (aic7xxx_verbose & VERBOSE_PROBE) { printk(" %d instructions downloaded\n", downloaded); } + if (aic7xxx_dump_sequencer) + aic7xxx_print_sequencer(p, downloaded); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_print_sequencer + * + * Description: + * Print the contents of the sequencer memory to the screen. + *-F*************************************************************************/ +static void +aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded) +{ + int i, k, temp; + + aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL); + aic_outb(p, 0, SEQADDR0); + aic_outb(p, 0, SEQADDR1); + + k = 0; + for (i=0; i < downloaded; i++) + { + if ( k == 0 ) + printk("%03x: ", i); + temp = aic_inb(p, SEQRAM); + temp |= (aic_inb(p, SEQRAM) << 8); + temp |= (aic_inb(p, SEQRAM) << 16); + temp |= (aic_inb(p, SEQRAM) << 24); + printk("%08x", temp); + if ( ++k == 8 ) + { + printk("\n"); + k = 0; + } + else + printk(" "); + } + aic_outb(p, 0, SEQADDR0); + aic_outb(p, 0, SEQADDR1); + aic_outb(p, FASTMODE | FAILDIS, SEQCTL); + unpause_sequencer(p, TRUE); + mdelay(1); + pause_sequencer(p); + aic_outb(p, FASTMODE, SEQCTL); + printk("\n"); } /*+F************************************************************************* @@ -1613,23 +2034,14 @@ * aic7xxx_delay * * Description: - * Delay for specified amount of time. We use udelay because the timer + * Delay for specified amount of time. We use mdelay because the timer * interrupt is not guaranteed to be enabled. This will cause an * infinite loop since jiffies (clock ticks) is not updated. *-F*************************************************************************/ static void aic7xxx_delay(int seconds) { - unsigned int i; - - /* - * Call udelay() for 1 millisecond inside a loop for - * the requested amount of seconds. - */ - for (i=0; i < seconds*1000; i++) - { - udelay(1000); /* Delay for 1 millisecond. */ - } + mdelay(seconds * 1000); } /*+F************************************************************************* @@ -1661,149 +2073,324 @@ return(bp); } - /*+F************************************************************************* * Function: - * aic7xxx_scsirate + * aic7xxx_find_syncrate * * Description: * Look up the valid period to SCSIRATE conversion in our table *-F*************************************************************************/ -static unsigned char -aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate, - unsigned char *period, unsigned char *offset, int target, int channel, - int set) +static struct aic7xxx_syncrate * +aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period, + unsigned int maxsync) { - int i = num_aic7xxx_syncrates; - unsigned char response_period; - unsigned char tindex; - unsigned short target_mask; - unsigned char lun; - - tindex = target | (channel << 3); - target_mask = 0x01 << tindex; - lun = aic_inb(p, SCB_TCL) & 0x07; - - response_period = *period; + struct aic7xxx_syncrate *syncrate; - /* - * If the offset is 0, then the device is requesting asynchronous - * transfers. - */ - if ((*period != 0) && (*offset != 0)) + syncrate = &aic7xxx_syncrates[maxsync]; + while ( (syncrate->rate[0] != NULL) && + (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) ) { - for (i = 0; i < num_aic7xxx_syncrates; i++) + if ( *period <= syncrate->period ) { - if (*period <= aic7xxx_syncrates[i].period) + /* + * When responding to a target that requests sync, the requested rate + * may fall between two rates that we can output, but still be a rate + * that we can receive. Because of this, we want to respond with the + * same rate that it sent to us even if the persiod we use to send + * data to it is lower. Only lower the response period if we must. + */ + if(syncrate == &aic7xxx_syncrates[maxsync]) { - /* - * Watch out for Ultra speeds when ultra is not enabled and - * vice-versa. - */ - if (!(p->type & AHC_ULTRA) && - (aic7xxx_syncrates[i].rate & ULTRA_SXFR)) - { - /* - * This should only happen if the drive is the first to negotiate - * and chooses a high rate. We'll just move down the table until - * we hit a non ultra speed. - */ - continue; - } - *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F); - *period = aic7xxx_syncrates[i].period; - - /* - * When responding to a target that requests - * sync, that rate may fall between two rates - * that we can output, but still be a rate - * that we can receive. Because of this, - * we may want to respond to the target with - * the same rate that it sent to us even - * if the period we use to send data to it - * is lower. Only lower the response period - * if we must. - */ - if ((i == 0) || - ((aic7xxx_syncrates[i-1].rate & ULTRA_SXFR) != 0 - && (p->type & AHC_ULTRA) == 0)) - { - response_period = *period; - } - - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) ) - { - printk(INFO_LEAD "Synchronous at %sMHz, " - "offset %d.\n", p->host_no, channel, target, lun, - aic7xxx_syncrates[i].english, *offset); - p->dev_flags[tindex] &= ~ DEVICE_PRINT_SDTR; - } - break; + *period = syncrate->period; } + break; } + syncrate++; } - - if (i >= num_aic7xxx_syncrates) + if ( (*period == 0) || (syncrate->rate[0] == NULL) || + ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) ) { /* - * Use asynchronous transfers. + * Use async transfers for this target */ - *scsirate = 0; *period = 0; - *offset = 0; - response_period = 0; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) ) - { - printk(INFO_LEAD "Using asynchronous transfers.\n", - p->host_no, channel, target, lun); - p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR; - } + syncrate = NULL; } + return (syncrate); +} - /* - * Ensure Ultra mode is set properly for this target. - */ - if ( (*scsirate != 0) && - (aic7xxx_syncrates[i].rate & ULTRA_SXFR) ) + +/*+F************************************************************************* + * Function: + * aic7xxx_find_period + * + * Description: + * Look up the valid SCSIRATE to period conversion in our table + *-F*************************************************************************/ +static unsigned int +aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate, + unsigned int maxsync) +{ + struct aic7xxx_syncrate *syncrate; + + if ((p->features & AHC_ULTRA2) != 0) { - p->ultraenb |= target_mask; + scsirate &= SXFR_ULTRA2; } else { - p->ultraenb &= ~target_mask; + scsirate &= SXFR; } - if (set) - { - unsigned char sxfrctl0; - sxfrctl0 = aic_inb(p, SXFRCTL0); - sxfrctl0 &= ~FAST20; - if (p->ultraenb & target_mask) + syncrate = &aic7xxx_syncrates[maxsync]; + while (syncrate->rate[0] != NULL) + { + if ((p->features & AHC_ULTRA2) != 0) { - sxfrctl0 |= FAST20; + if (syncrate->sxfr_ultra2 == 0) + break; + else if (scsirate == syncrate->sxfr_ultra2) + return (syncrate->period); + } + else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR)) + { + return (syncrate->period); } - aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB); - aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1); - aic_outb(p, sxfrctl0, SXFRCTL0); + syncrate++; } - return(response_period); + return (0); /* async */ } /*+F************************************************************************* * Function: - * scbq_init + * aic7xxx_validate_offset * * Description: - * SCB queue initialization. - * + * Set a valid offset value for a particular card in use and transfer + * settings in use. *-F*************************************************************************/ -static inline void -scbq_init(volatile scb_queue_type *queue) +static void +aic7xxx_validate_offset(struct aic7xxx_host *p, + struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide) { - queue->head = NULL; - queue->tail = NULL; + unsigned int maxoffset; + + /* Limit offset to what the card (and device) can do */ + if (syncrate == NULL) + { + maxoffset = 0; + } + else if (p->features & AHC_ULTRA2) + { + maxoffset = MAX_OFFSET_ULTRA2; + } + else + { + if (wide) + maxoffset = MAX_OFFSET_16BIT; + else + maxoffset = MAX_OFFSET_8BIT; + } + *offset = MIN(*offset, maxoffset); +} + +/*+F************************************************************************* + * Function: + * aic7xxx_set_syncrate + * + * Description: + * Set the actual syncrate down in the card and in our host structs + *-F*************************************************************************/ +static void +aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate, + int target, int channel, unsigned int period, unsigned int offset, + unsigned int type) +{ + unsigned char tindex; + unsigned short target_mask; + unsigned char lun; + unsigned int old_period, old_offset; + + tindex = target | (channel << 3); + target_mask = 0x01 << tindex; + lun = aic_inb(p, SCB_TCL) & 0x07; + + if (syncrate == NULL) + { + period = 0; + offset = 0; + } + + old_period = p->transinfo[tindex].cur_period; + old_offset = p->transinfo[tindex].cur_offset; + + + if (type & AHC_TRANS_CUR) + { + unsigned int scsirate; + + scsirate = aic_inb(p, TARG_SCSIRATE + tindex); + if (p->features & AHC_ULTRA2) + { + scsirate &= ~SXFR_ULTRA2; + if (syncrate != NULL) + { + scsirate |= syncrate->sxfr_ultra2; + } + if (type & AHC_TRANS_ACTIVE) + { + aic_outb(p, offset, SCSIOFFSET); + } + aic_outb(p, offset, TARG_OFFSET + tindex); + } + else /* Not an Ultra2 controller */ + { + scsirate &= ~(SXFR|SOFS); + p->ultraenb &= ~target_mask; + if (syncrate != NULL) + { + if (syncrate->sxfr & ULTRA_SXFR) + { + p->ultraenb |= target_mask; + } + scsirate |= (syncrate->sxfr & SXFR); + scsirate |= (offset & SOFS); + } + if (type & AHC_TRANS_ACTIVE) + { + unsigned char sxfrctl0; + + sxfrctl0 = aic_inb(p, SXFRCTL0); + sxfrctl0 &= ~FAST20; + if (p->ultraenb & target_mask) + sxfrctl0 |= FAST20; + aic_outb(p, sxfrctl0, SXFRCTL0); + } + aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB); + aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 ); + } + if (type & AHC_TRANS_ACTIVE) + { + aic_outb(p, scsirate, SCSIRATE); + } + aic_outb(p, scsirate, TARG_SCSIRATE + tindex); + p->transinfo[tindex].cur_period = period; + p->transinfo[tindex].cur_offset = offset; + if ( !(type & AHC_TRANS_QUITE) && + (aic7xxx_verbose & VERBOSE_NEGOTIATION) && + (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) ) + { + if (offset) + { + int rate_mod = (scsirate & WIDEXFER) ? 1 : 0; + + printk(INFO_LEAD "Synchronous at %s Mbyte/sec, " + "offset %d.\n", p->host_no, channel, target, lun, + syncrate->rate[rate_mod], offset); + } + else + { + printk(INFO_LEAD "Using asynchronous transfers.\n", + p->host_no, channel, target, lun); + } + p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR; + } + } + + if (type & AHC_TRANS_GOAL) + { + p->transinfo[tindex].goal_period = period; + p->transinfo[tindex].goal_offset = offset; + } + + if (type & AHC_TRANS_USER) + { + p->transinfo[tindex].user_period = period; + p->transinfo[tindex].user_offset = offset; + } +} + +/*+F************************************************************************* + * Function: + * aic7xxx_set_width + * + * Description: + * Set the actual width down in the card and in our host structs + *-F*************************************************************************/ +static void +aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun, + unsigned int width, unsigned int type) +{ + unsigned char tindex; + unsigned short target_mask; + unsigned int old_width, new_offset; + + tindex = target | (channel << 3); + target_mask = 1 << tindex; + + old_width = p->transinfo[tindex].cur_width; + + if (p->features & AHC_ULTRA2) + new_offset = MAX_OFFSET_ULTRA2; + else if (width == MSG_EXT_WDTR_BUS_16_BIT) + new_offset = MAX_OFFSET_16BIT; + else + new_offset = MAX_OFFSET_8BIT; + + if (type & AHC_TRANS_CUR) + { + unsigned char scsirate; + + scsirate = aic_inb(p, TARG_SCSIRATE + tindex); + + scsirate &= ~WIDEXFER; + if (width == MSG_EXT_WDTR_BUS_16_BIT) + scsirate |= WIDEXFER; + + aic_outb(p, scsirate, TARG_SCSIRATE + tindex); + + if (type & AHC_TRANS_ACTIVE) + aic_outb(p, scsirate, SCSIRATE); + + p->transinfo[tindex].cur_width = width; + + if ((aic7xxx_verbose & VERBOSE_NEGOTIATION2) && + (p->dev_flags[tindex] & DEVICE_PRINT_WDTR)) + { + printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target, + lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" ); + p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR; + } + } + + if (type & AHC_TRANS_GOAL) + p->transinfo[tindex].goal_width = width; + if (type & AHC_TRANS_USER) + p->transinfo[tindex].user_width = width; + + /* + * Having just set the width, the SDTR should come next, and we need a valid + * offset for the SDTR. So, we make sure we put a valid one in here now as + * the goal_offset. + */ + if (p->transinfo[tindex].goal_offset) + p->transinfo[tindex].goal_offset = new_offset; + +} + +/*+F************************************************************************* + * Function: + * scbq_init + * + * Description: + * SCB queue initialization. + * + *-F*************************************************************************/ +static void +scbq_init(volatile scb_queue_type *queue) +{ + queue->head = NULL; + queue->tail = NULL; } /*+F************************************************************************* @@ -1837,7 +2424,7 @@ * Remove an SCB from the head of the list. * *-F*************************************************************************/ -static __inline struct aic7xxx_scb * +static inline struct aic7xxx_scb * scbq_remove_head(volatile scb_queue_type *queue) { struct aic7xxx_scb * scbp; @@ -2030,7 +2617,7 @@ * Description: * Set the specified target busy. *-F*************************************************************************/ -static __inline void +static inline void aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag; @@ -2044,7 +2631,7 @@ * Returns the index of the busy target, and optionally sets the * target inactive. *-F*************************************************************************/ -static __inline unsigned char +static inline unsigned char aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl, int unbusy) { @@ -2100,47 +2687,47 @@ * Description: * Get an SCB from the free list or by allocating a new one. *-F*************************************************************************/ -static struct aic7xxx_scb * -aic7xxx_allocate_scb(struct aic7xxx_host *p, int force_alloc) +static int +aic7xxx_allocate_scb(struct aic7xxx_host *p) { struct aic7xxx_scb *scbp = NULL; int scb_size = sizeof(struct aic7xxx_scb) + sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG; int i; + int step = PAGE_SIZE / 1024; unsigned long scb_count = 0; struct hw_scatterlist *hsgp; struct aic7xxx_scb *scb_ap; + unsigned long temp; - if (force_alloc == FALSE) - { - scbp = scbq_remove_head(&p->scb_data->free_scbs); - if (scbp != NULL) - return(scbp); - } - /* - * Either there wasn't an SCB or this is a strictly allocation call - */ - if (p->scb_data->numscbs < p->scb_data->maxscbs) { - /* - * Optimize for 30 scbs at a time, but allow a final allocation of - * fewer than 30 scbs. Except on 64 bit platforms, we optimize for - * 29 SCBs at a time because a pointer is 4 bytes larger and we don't - * want to overrun this suppossedly 32K allocation to 64K and waste - * tons of space. + * Calculate the optimal number of SCBs to allocate. + * + * NOTE: This formula works because the sizeof(sg_array) is always + * 1024. Therefore, scb_size * i would always be > PAGE_SIZE * + * (i/step). The (i-1) allows the left hand side of the equation + * to grow into the right hand side to a point of near perfect + * efficiency since scb_size * (i -1) is growing slightly faster + * than the right hand side. If the number of SG array elements + * is changed, this function may not be near so efficient any more. */ - if( sizeof(void *) == sizeof(int) ) - scb_count = MIN(30, p->scb_data->maxscbs - p->scb_data->numscbs); - else - scb_count = MIN(29, p->scb_data->maxscbs - p->scb_data->numscbs); - + for ( i=step;; i *= 2 ) + { + if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) ) + { + i /= 2; + break; + } + } + scb_count = MIN( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs); scb_ap = (struct aic7xxx_scb *)kmalloc(scb_size * scb_count, GFP_ATOMIC); if (scb_ap != NULL) { - if (aic7xxx_verbose & VERBOSE_QUEUE) +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) { if (p->scb_data->numscbs == 0) printk(INFO_LEAD "Allocating initial %ld SCB structures.\n", @@ -2149,8 +2736,12 @@ printk(INFO_LEAD "Allocating %ld additional SCB structures.\n", p->host_no, -1, -1, -1, scb_count); } +#endif memset(scb_ap, 0, scb_count * scb_size); - hsgp = (struct hw_scatterlist *) &scb_ap[scb_count]; + temp = (unsigned long) &scb_ap[scb_count]; + temp += 1023; + temp &= ~1023; + hsgp = (struct hw_scatterlist *)temp; for (i=0; i < scb_count; i++) { scbp = &scb_ap[i]; @@ -2164,20 +2755,14 @@ p->scb_data->scb_array[p->scb_data->numscbs++] = scbp; scbq_insert_head(&p->scb_data->free_scbs, scbp); } + scbp->kmalloc_ptr = scb_ap; } else { - return(NULL); + return(0); } } - if (force_alloc == TRUE) - { - return((struct aic7xxx_scb *)scb_count); - } - else - { - return(scbq_remove_head(&p->scb_data->free_scbs)); - } + return(scb_count); } /*+F************************************************************************* @@ -2189,7 +2774,7 @@ * to queue completed commands, then call scsi_done() on them when * we're finished. This function queues the completed commands. *-F*************************************************************************/ -static inline void +static void aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { cmd->host_scribble = (char *)p->completeq.head; @@ -2203,7 +2788,7 @@ * Description: * Process the completed command queue. *-F*************************************************************************/ -static inline void +static void aic7xxx_done_cmds_complete(struct aic7xxx_host *p) { Scsi_Cmnd *cmd; @@ -2276,17 +2861,15 @@ } if (scb->flags & SCB_RESET) { - cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | - (cmd->result & 0xffff); + cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff); } else if (scb->flags & SCB_ABORT) { - cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | - (cmd->result & 0xffff); + cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff); } - if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) + else if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) { - if(cmd->cmnd[0] == INQUIRY) + if ( (cmd->cmnd[0] == INQUIRY) && (cmd->result == DID_OK) ) { char *buffer; @@ -2304,35 +2887,65 @@ #define WIDE_INQUIRY_BITS 0x60 #define SYNC_INQUIRY_BITS 0x10 if ( (buffer[7] & WIDE_INQUIRY_BITS) && - (p->needwdtr_copy & (1<type & AHC_WIDE) ) + (p->features & AHC_WIDE) ) { p->needwdtr |= (1<needwdtr_copy |= (1<syncinfo[tindex].offset = MAX_OFFSET_16BIT; + if ( (p->flags & AHC_SEEPROM_FOUND) && + (p->transinfo[tindex].user_width != MSG_EXT_WDTR_BUS_16_BIT) ) + p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT; + else + p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT; } else { p->needwdtr &= ~(1<needwdtr_copy &= ~(1<syncinfo[tindex].offset = MAX_OFFSET_8BIT; + pause_sequencer(p); + aic7xxx_set_width(p, cmd->target, cmd->channel, cmd->lun, + MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE | + AHC_TRANS_GOAL | + AHC_TRANS_CUR) ); + unpause_sequencer(p, FALSE); } if (buffer[7] & SYNC_INQUIRY_BITS) { p->needsdtr |= (1<needsdtr_copy |= (1<flags & AHC_SEEPROM_FOUND) + p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period; + else if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_period = + aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period; + else if (p->features & AHC_ULTRA) + p->transinfo[tindex].goal_period = + aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period; + else + p->transinfo[tindex].goal_period = + aic7xxx_syncrates[AHC_SYNCRATE_FAST].period; + + if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; + else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) + p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + else + p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; } else { p->needsdtr &= ~(1<needsdtr_copy &= ~(1<transinfo[tindex].goal_period = 0; + p->transinfo[tindex].goal_offset = 0; } p->dev_flags[tindex] |= DEVICE_SCANNED; + p->dev_flags[tindex] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR; #undef WIDE_INQUIRY_BITS #undef SYNC_INQUIRY_BITS } } - if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0) + else if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0) { unsigned short mask; int message_error = FALSE; @@ -2355,7 +2968,7 @@ p->wdtr_pending &= ~mask; if (message_error) { - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && + if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) ) { printk(INFO_LEAD "Device failed to complete Wide Negotiation " @@ -2375,7 +2988,7 @@ p->sdtr_pending &= ~mask; if (message_error) { - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && + if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) ) { printk(INFO_LEAD "Device failed to complete Sync Negotiation " @@ -2411,12 +3024,17 @@ { scbq_insert_tail(&p->waiting_scbs, scbp); } - } - if ( (queue_depth > p->dev_active_cmds[tindex]) && scbp ) - { - scbp = scbq_remove_head(&p->delayed_scbs[tindex]); - if (scbp) - scbq_insert_tail(&p->waiting_scbs, scbp); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n", + p->host_no, CTL_OF_SCB(scbp)); +#endif + if (queue_depth > p->dev_active_cmds[tindex]) + { + scbp = scbq_remove_head(&p->delayed_scbs[tindex]); + if (scbp) + scbq_insert_tail(&p->waiting_scbs, scbp); + } } } if ( !(scb->tag_action) && (p->tagenable & (1<hscb->target_channel_lun, TRUE); } -#ifdef AIC7XXX_PROC_STATS { int actual; @@ -2454,11 +3071,17 @@ if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK)) { struct aic7xxx_xferstats *sp; +#ifdef AIC7XXX_PROC_STATS long *ptr; int x; +#endif /* AIC7XXX_PROC_STATS */ sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7]; sp->xfers++; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if ( (sp->xfers > 16) && (aic7xxx_verbose > 0xffff) ) + aic7xxx_verbose &= 0xffff; +#endif /* * For block devices, cmd->request.cmd is always == either READ or @@ -2471,14 +3094,19 @@ { sp->w_total++; sp->w_total512 += (actual >> 9); +#ifdef AIC7XXX_PROC_STATS ptr = sp->w_bins; +#endif /* AIC7XXX_PROC_STATS */ } else { sp->r_total++; sp->r_total512 += (actual >> 9); +#ifdef AIC7XXX_PROC_STATS ptr = sp->r_bins; +#endif /* AIC7XXX_PROC_STATS */ } +#ifdef AIC7XXX_PROC_STATS for (x = 9; x <= 17; x++) { if (actual < (1 << x)) @@ -2491,10 +3119,9 @@ { ptr[x - 9]++; } +#endif /* AIC7XXX_PROC_STATS */ } } -#endif /* AIC7XXX_PROC_STATS */ - aic7xxx_free_scb(p, scb); aic7xxx_queue_cmd_complete(p, cmd); @@ -2676,7 +3303,10 @@ { p->qinfifo[qinpos++] = SCB_LIST_NULL; } - aic_outb(p, p->qinfifonext, KERNEL_QINPOS); + if (p->features & AHC_QUEUE_REGS) + aic_outb(p, p->qinfifonext, HNSCB_QOFF); + else + aic_outb(p, p->qinfifonext, KERNEL_QINPOS); return (found); } @@ -2749,7 +3379,7 @@ { case 0: min_target = 0; - max_target = (p->type & AHC_WIDE) ? 15 : 7; + max_target = (p->features & AHC_WIDE) ? 15 : 7; break; case 1: min_target = 8; @@ -2758,7 +3388,7 @@ case ALL_CHANNELS: default: min_target = 0; - max_target = (p->type & (AHC_TWIN|AHC_WIDE)) ? 15 : 7; + max_target = (p->features & (AHC_TWIN|AHC_WIDE)) ? 15 : 7; break; } } @@ -2821,7 +3451,7 @@ j = 0; prev_scbp = NULL; scbp = p->delayed_scbs[i].head; - while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) ) + while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) ) { prev_scbp = scbp; scbp = scbp->q_next; @@ -2846,7 +3476,7 @@ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE; } } - if ( j > p->scb_data->numscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET)) printk(WARN_LEAD "Yikes!! There's a loop in the " @@ -2880,7 +3510,7 @@ j = 0; prev_scbp = NULL; scbp = p->waiting_scbs.head; - while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) ) + while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) ) { prev_scbp = scbp; scbp = scbp->q_next; @@ -2905,7 +3535,7 @@ prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE; } } - if ( j > p->scb_data->numscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET)) printk(WARN_LEAD "Yikes!! There's a loop in the " @@ -2927,7 +3557,7 @@ next = aic_inb(p, WAITING_SCBH); /* Start at head of list. */ prev = SCB_LIST_NULL; j = 0; - while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) ) + while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) ) { aic_outb(p, next, SCBPTR); scb_index = aic_inb(p, SCB_TAG); @@ -2975,7 +3605,7 @@ } } } - if ( j > p->scb_data->maxhscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { printk(WARN_LEAD "Yikes!! There is a loop in the waiting for " "selection list!\n", p->host_no, channel, target, lun); @@ -2990,13 +3620,14 @@ if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS)) printk(INFO_LEAD "Cleaning disconnected scbs " "list.\n", p->host_no, channel, target, lun); + if (p->features & AHC_PAGESCBS) { unsigned char next, prev, scb_index; next = aic_inb(p, DISCONNECTED_SCBH); prev = SCB_LIST_NULL; j = 0; - while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) ) + while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) ) { aic_outb(p, next, SCBPTR); scb_index = aic_inb(p, SCB_TAG); @@ -3029,7 +3660,7 @@ } } } - if ( j > p->scb_data->maxhscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { printk(WARN_LEAD "Yikes!! There is a loop in the disconnected list!\n", p->host_no, channel, target, lun); @@ -3041,15 +3672,23 @@ * Walk the free list making sure no entries on the free list have * a valid SCB_TAG value or SCB_CONTROL byte. */ + if (p->features & AHC_PAGESCBS) { unsigned char next; j = 0; next = aic_inb(p, FREE_SCBH); - while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) ) + if ( (next >= p->scb_data->maxhscbs) && (next != SCB_LIST_NULL) ) + { + printk(WARN_LEAD "Bogus FREE_SCBH!.\n", p->host_no, channel, + target, lun); + init_lists = TRUE; + next = SCB_LIST_NULL; + } + while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) ) { aic_outb(p, next, SCBPTR); - if ( aic_inb(p, SCB_TAG) < p->scb_data->numscbs ) + if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs) { printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel, target, lun); @@ -3063,7 +3702,7 @@ next = aic_inb(p, SCB_NEXT); } } - if ( j > p->scb_data->maxhscbs ) + if ( j > (p->scb_data->maxscbs + 1) ) { printk(WARN_LEAD "Yikes!! There is a loop in the free list!\n", p->host_no, channel, target, lun); @@ -3081,7 +3720,7 @@ aic_outb(p, SCB_LIST_NULL, WAITING_SCBH); aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH); } - for (i = p->scb_data->maxhscbs; i >= 0; --i) + for (i = p->scb_data->maxhscbs - 1; i >= 0; i--) { unsigned char scbid; @@ -3157,7 +3796,7 @@ aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0); aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR | CLRPHASECHG | CLRREQINIT, CLRSINT1); - aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT, CLRINT); + aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT); } /*+F************************************************************************* @@ -3170,26 +3809,31 @@ static void aic7xxx_reset_current_bus(struct aic7xxx_host *p) { - unsigned char scsiseq; /* Disable reset interrupts. */ aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1); + /* Turn off the bus' current operations, after all, we shouldn't have any + * valid commands left to cause a RSELI and SELO once we've tossed the + * bus away with this reset, so we might as well shut down the sequencer + * until the bus is restarted as oppossed to saving the current settings + * and restoring them (which makes no sense to me). */ + /* Turn on the bus reset. */ - scsiseq = aic_inb(p, SCSISEQ); - aic_outb(p, scsiseq | SCSIRSTO, SCSISEQ); + aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ); + while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0) + mdelay(5); - udelay(5000); + mdelay(10); /* Turn off the bus reset. */ - aic_outb(p, scsiseq & ~SCSIRSTO, SCSISEQ); + aic_outb(p, 0, SCSISEQ); + mdelay(5); aic7xxx_clear_intstat(p); - /* Re-enable reset interrupts. */ aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1); - udelay(2000); } /*+F************************************************************************* @@ -3202,7 +3846,7 @@ static void aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset) { - unsigned long offset, offset_max; + unsigned long offset_min, offset_max; unsigned char sblkctl; int cur_channel; @@ -3215,48 +3859,52 @@ { p->needsdtr |= (p->needsdtr_copy & 0xFF00); p->sdtr_pending &= 0x00FF; - offset = TARG_SCRATCH + 8; - offset_max = TARG_SCRATCH + 16; + offset_min = 8; + offset_max = 16; } else { - if (p->type & AHC_WIDE) + if (p->features & AHC_WIDE) { p->needsdtr = p->needsdtr_copy; p->needwdtr = p->needwdtr_copy; p->sdtr_pending = 0x0; p->wdtr_pending = 0x0; - offset = TARG_SCRATCH; - offset_max = TARG_SCRATCH + 16; + offset_min = 0; + offset_max = 16; } else { /* Channel A */ p->needsdtr |= (p->needsdtr_copy & 0x00FF); p->sdtr_pending &= 0xFF00; - offset = TARG_SCRATCH; - offset_max = TARG_SCRATCH + 8; + offset_min = 0; + offset_max = 8; } } - while (offset < offset_max) + while (offset_min < offset_max) { /* * Revert to async/narrow transfers until we renegotiate. */ - u_char targ_scratch; - - targ_scratch = aic_inb(p, offset); - targ_scratch &= SXFR; - aic_outb(p, targ_scratch, offset++); + aic_outb(p, 0, TARG_SCSIRATE + offset_min); + if (p->features & AHC_ULTRA2) + { + aic_outb(p, 0, TARG_OFFSET + offset_min); + } + offset_min++; } /* * Reset the bus and unpause/restart the controller */ sblkctl = aic_inb(p, SBLKCTL); - cur_channel = (sblkctl & SELBUSB) >> 3; - if (cur_channel != channel) + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) + cur_channel = (sblkctl & SELBUSB) >> 3; + else + cur_channel = 0; + if ( (cur_channel != channel) && (p->features & AHC_TWIN) ) { /* * Case 1: Command for another bus is active @@ -3273,7 +3921,7 @@ { aic7xxx_reset_current_bus(p); } - aic_outb(p, 0, SCSISEQ); + aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ); aic7xxx_clear_intstat(p); aic_outb(p, sblkctl, SBLKCTL); } @@ -3294,7 +3942,7 @@ { aic7xxx_reset_current_bus(p); } - aic_outb(p, 0, SCSISEQ); + aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ); aic7xxx_clear_intstat(p); } if (aic7xxx_verbose & VERBOSE_RESET_RETURN) @@ -3305,7 +3953,13 @@ */ aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL); - if ( !(p->type & AHC_TWIN) ) + /* + * Convince Mid Level SCSI code to leave us be for a little bit... + */ + p->last_reset = jiffies; + p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY)); + + if ( !(p->features & AHC_TWIN) ) { restart_sequencer(p); } @@ -3321,7 +3975,7 @@ * Scan the awaiting_scbs queue downloading and starting as many * scbs as we can. *-F*************************************************************************/ -static inline void +static void aic7xxx_run_waiting_queues(struct aic7xxx_host *p) { struct aic7xxx_scb *scb; @@ -3346,23 +4000,38 @@ tindex = TARGET_INDEX(scb->cmd); if ( !scb->tag_action && (p->tagenable & (1< 0xffff) + printk(INFO_LEAD "Reducing Queue depth for untagged command.\n", + p->host_no, CTL_OF_SCB(scb)); +#endif p->dev_temp_queue_depth[tindex] = 1; } if ( (p->dev_active_cmds[tindex] >= p->dev_temp_queue_depth[tindex]) || (p->dev_last_reset[tindex] >= (jiffies - (4 * HZ))) ) { - scbq_insert_tail(&p->delayed_scbs[tindex], scb); - if ( !(p->dev_timer[tindex].expires) && - !(p->dev_active_cmds[tindex]) ) - { - p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ); - add_timer(&p->dev_timer[tindex]); - } +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Moving SCB to Delayed Queue.\n", + p->host_no, CTL_OF_SCB(scb)); +#endif + scbq_insert_tail(&p->delayed_scbs[tindex], scb); + if ( !(p->dev_timer[tindex].expires) && + !(p->dev_active_cmds[tindex]) ) + { + p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ); + add_timer(&p->dev_timer[tindex]); + } } else { scb->flags &= ~SCB_WAITINGQ; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Sending command %d/0x%x to QINFIFO\n", p->host_no, + CTL_OF_SCB(scb), scb->hscb->tag, scb->flags); +#endif p->dev_active_cmds[tindex]++; p->activescbs++; if ( !(scb->tag_action) ) @@ -3375,15 +4044,95 @@ } if (sent) { - pause_sequencer(p); - aic_outb(p, p->qinfifonext, KERNEL_QINPOS); - unpause_sequencer(p, FALSE); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + { + printk(INFO_LEAD "Sending commands to QINFIFO\n", p->host_no, + -1, -1, -1); + if ( (p->isr_count < 16) && (aic7xxx_panic_on_abort) && + (p->flags & AHC_PAGESCBS) ) + aic7xxx_check_scbs(p, "While sending commands to QINFIFO"); + } +#endif + if (p->features & AHC_QUEUE_REGS) + aic_outb(p, p->qinfifonext, HNSCB_QOFF); + else + { + pause_sequencer(p); + aic_outb(p, p->qinfifonext, KERNEL_QINPOS); + unpause_sequencer(p, FALSE); + } if (p->activescbs > p->max_activescbs) p->max_activescbs = p->activescbs; } DRIVER_UNLOCK } +#ifdef CONFIG_PCI + +#define DPE 0x80 +#define SSE 0x40 +#define RMA 0x20 +#define RTA 0x10 +#define STA 0x08 +#define DPR 0x01 + +/*+F************************************************************************* + * Function: + * aic7xxx_pci_intr + * + * Description: + * Check the scsi card for PCI errors and clear the interrupt + * + * NOTE: If you don't have this function and a 2940 card encounters + * a PCI error condition, the machine will end up locked as the + * interrupt handler gets slammed with non-stop PCI error interrupts + *-F*************************************************************************/ +static void +aic7xxx_pci_intr(struct aic7xxx_host *p) +{ + unsigned char status1; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1); +#else + pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, + PCI_STATUS + 1, &status1); +#endif + + if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Data Parity Error during PCI address or PCI write" + "phase.\n", p->host_no, -1, -1, -1); + if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Signal System Error Detected\n", p->host_no, + -1, -1, -1); + if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no, + -1, -1, -1); + if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no, + -1, -1, -1); + if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no, + -1, -1, -1); + if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) + printk(WARN_LEAD "Data Parity Error has been reported via PCI pin " + "PERR#\n", p->host_no, -1, -1, -1); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1); +#else + pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, + PCI_STATUS + 1, status1); +#endif + if (status1 & (DPR|RMA|RTA)) + aic_outb(p, CLRPARERR, CLRINT); + + if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) ) + aic7xxx_panic_abort(p, NULL); + +} +#endif /* CONFIG_PCI */ /*+F************************************************************************* * Function: @@ -3554,12 +4303,11 @@ aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel) { unsigned short targ_mask; - unsigned char targ_scratch; - int scratch_offset = target; + unsigned char tindex = target; - scratch_offset += channel << 3; + tindex |= ((channel & 0x01) << 3); - targ_mask = (0x01 << scratch_offset); + targ_mask = (0x01 << tindex); /* * Go back to async/narrow transfers and renegotiate. */ @@ -3567,9 +4315,9 @@ p->needwdtr |= (p->needwdtr_copy & targ_mask); p->sdtr_pending &= ~targ_mask; p->wdtr_pending &= ~targ_mask; - targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset); - targ_scratch &= SXFR; - aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset); + aic_outb(p, 0, TARG_SCSIRATE + tindex); + if (p->features & AHC_ULTRA2) + aic_outb(p, 0, TARG_OFFSET + tindex); aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL); if (aic7xxx_verbose & VERBOSE_RESET_PROCESS) printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel, @@ -3589,20 +4337,30 @@ { struct aic7xxx_scb *scb; unsigned short target_mask; - unsigned char target, scratch_offset, lun; + unsigned char target, lun, tindex; unsigned char queue_flag = FALSE; char channel; target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f); - channel = (aic_inb(p, SBLKCTL) >> 3) & 0x01; - scratch_offset = target + (channel << 3); + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) + channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + else + channel = 0; + tindex = target + (channel << 3); lun = aic_inb(p, SAVED_TCL) & 0x07; - target_mask = (0x01 << scratch_offset); + target_mask = (0x01 << tindex); + /* + * Go ahead and clear the SEQINT now, that avoids any interrupt race + * conditions later on in case we enable some other interrupt. + */ + aic_outb(p, CLRSEQINT, CLRINT); switch (intstat & SEQINT_MASK) { case NO_MATCH: { + aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), + SCSISEQ); printk(WARN_LEAD "No active SCB for reconnecting target - Issuing " "BUS DEVICE RESET.\n", p->host_no, channel, target, lun); printk(WARN_LEAD " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n", @@ -3663,12 +4421,11 @@ p->msg_len = 0; p->msg_index = 0; - /* - * We have to clear the SEQINT *BEFORE* we set the REQINIT handler - * active or else VLB and edge triggered EISA cards could loose the - * first REQINIT and cause a bus hang/reset cycle. - */ - aic_outb(p, CLRSEQINT, CLRINT); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no, + channel, target, lun); +#endif /* * To actually receive the message, simply turn on @@ -3691,13 +4448,11 @@ * or WDTR message for this target. If we did, this is a * signal that the target is refusing negotiation. */ - unsigned char targ_scratch; unsigned char scb_index; unsigned char last_msg; scb_index = aic_inb(p, SCB_TAG); scb = p->scb_data->scb_array[scb_index]; - targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset); last_msg = aic_inb(p, LAST_MSG); if ( (last_msg == MSG_IDENTIFYFLAG) && @@ -3705,7 +4460,7 @@ !(scb->flags & SCB_MSGOUT_BITS) ) { if ((scb->tag_action == MSG_ORDERED_Q_TAG) && - (p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS)) + (p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS)) { /* * OK...the device seems able to accept tagged commands, but @@ -3727,7 +4482,7 @@ aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO); } else if ( (scb->tag_action == MSG_SIMPLE_Q_TAG) && - !(p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS) ) + !(p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS) ) { unsigned char i, reset = 0; struct aic7xxx_scb *scbp; @@ -3742,8 +4497,8 @@ */ p->tagenable &= ~target_mask; p->orderedtag &= ~target_mask; - p->dev_max_queue_depth[scratch_offset] = - p->dev_temp_queue_depth[scratch_offset] = 1; + p->dev_max_queue_depth[tindex] = + p->dev_temp_queue_depth[tindex] = 1; /* * We set this command up as a bus device reset. However, we have * to clear the tag type as it's causing us problems. We shouldnt @@ -3792,40 +4547,34 @@ /* * note 8bit xfers and clear flag */ - targ_scratch &= 0x7F; p->needwdtr &= ~target_mask; p->needwdtr_copy &= ~target_mask; p->wdtr_pending &= ~target_mask; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) + scb->flags &= ~SCB_MSGOUT_BITS; + aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT, + (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR)); + aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE); + if ( (p->needsdtr_copy & target_mask) && + !(p->sdtr_pending & target_mask) ) { - printk(INFO_LEAD "Refusing WIDE negotiation; using 8 bit " - "transfers.\n", p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } - scb->flags &= ~SCB_MSGOUT_WDTR_16BIT; - p->syncinfo[scratch_offset].offset = MAX_OFFSET_8BIT; - if (p->needsdtr_copy & target_mask) - p->needsdtr |= target_mask; + p->sdtr_pending |= target_mask; + scb->flags |= SCB_MSGOUT_SDTR; + aic_outb(p, HOST_MSG, MSG_OUT); + aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); + } } else if (scb->flags & SCB_MSGOUT_SDTR) { /* * note asynch xfers and clear flag */ - targ_scratch &= 0xF0; p->needsdtr &= ~target_mask; p->needsdtr_copy &= ~target_mask; p->sdtr_pending &= ~target_mask; - p->syncinfo[scratch_offset].period = 0; - p->syncinfo[scratch_offset].offset = 0; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_SDTR) ) - { - printk(INFO_LEAD "Refusing synchronous negotiation; using " - "asynchronous transfers.\n", p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_SDTR; - } + scb->flags &= ~SCB_MSGOUT_SDTR; + aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, + (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL)); } else if (aic7xxx_verbose & VERBOSE_SEQINT) { @@ -3835,8 +4584,6 @@ printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause. " "Ignoring.\n", p->host_no, channel, target, lun); } - aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset); - aic_outb(p, targ_scratch, SCSIRATE); } break; @@ -3851,11 +4598,15 @@ * the sequencer running in the common case of command completes * without error. The sequencer will have DMA'd the SCB back * up to us, so we can reference the drivers SCB array. + * + * Set the default return value to 0 indicating not to send + * sense. The sense code will change this if needed and this + * reduces code duplication. */ + aic_outb(p, 0, RETURN_1); scb_index = aic_inb(p, SCB_TAG); if (scb_index > p->scb_data->numscbs) { - aic_outb(p, 0, RETURN_1); printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n", p->host_no, channel, target, lun, intstat, scb_index); break; @@ -3863,12 +4614,6 @@ scb = p->scb_data->scb_array[scb_index]; hscb = scb->hscb; - /* - * Set the default return value to 0 indicating not to send - * sense. The sense code will change this if needed and this - * reduces code duplication. - */ - aic_outb(p, 0, RETURN_1); if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x," @@ -3937,7 +4682,6 @@ scb->sg_count = hscb->SG_segment_count = 1; scb->sg_length = sizeof(cmd->sense_buffer); - scb->flags &= ~SCB_MSGOUT_BITS; scb->tag_action = 0; /* * This problem could be caused if the target has lost power @@ -3945,12 +4689,67 @@ * so if needed, we'll re-negotiate while doing the sense cmd. * However, if this SCB already was attempting to negotiate, * then we assume this isn't the problem and skip this part. - * - * 1998/04/23 - We also don't want to set the flag if the - * original command was a TEST_UNIT_READY since that - * implies a SEND_SENSE anyway. */ - if (scb->cmd->cmnd[0] != TEST_UNIT_READY) +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS + if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) && + (p->dev_flags[tindex] & DEVICE_SCANNED) && + !(p->wdtr_pending & target_mask) && + !(p->sdtr_pending & target_mask) ) + { + p->needwdtr |= (p->needwdtr_copy & target_mask); + p->needsdtr |= (p->needsdtr_copy & target_mask); + } + else if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) || + (scb->cmd == p->dev_sdtr_cmnd[tindex]) ) + { + /* + * This is already a negotiation command, so we must have + * already done either WDTR or SDTR (or maybe both). So + * we simply check sdtr_pending and needsdtr to see if we + * should throw out SDTR on this command. + * + * Note: Don't check the needsdtr_copy here, instead just + * check to see if WDTR wiped out our SDTR and set needsdtr. + * Even if WDTR did wipe out SDTR and set needsdtr, if + * parse_msg() then turned around and started our SDTR + * in back to back fasion, then conclusion of that should + * have negated any needsdtr setting. That's why we only + * check needsdtr and sdtr_pending. + */ + scb->flags &= ~SCB_MSGOUT_BITS; + if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) && + !(p->sdtr_pending & target_mask) && + (p->needsdtr & target_mask) ) + { + p->sdtr_pending |= target_mask; + hscb->control |= MK_MESSAGE; + scb->flags |= SCB_MSGOUT_SDTR; + } + + /* + * This is the important part though. We are getting sense + * info back from this device. It's going into a fake + * command. We need to put that into the real command + * instead so that the mid level SCSI code can act upon it. + * So, when we set up these fake commands, the next pointer + * is used to point to the real command. Use that to change + * the address of our sense_buffer[] to the real command. + * However, don't do this if the real command is also a + * TEST_UNIT_READY as it will most likely pull down its own + * SENSE information anyway. + */ + if (cmd->next->cmnd[0] != TEST_UNIT_READY) + { + scb->sg_list[0].address = + cpu_to_le32(VIRT_TO_BUS(&cmd->next->sense_buffer[0])); + hscb->data_pointer = scb->sg_list[0].address; + } + } +#else + if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) && + !(scb->flags & SCB_MSGOUT_BITS) && + (scb->cmd->lun == 0) && + (p->dev_flags[TARGET_INDEX(scb->cmd)] & DEVICE_SCANNED) ) { if ( (p->needwdtr_copy & target_mask) && !(p->wdtr_pending & target_mask) && @@ -3959,7 +4758,7 @@ p->needwdtr |= target_mask; p->wdtr_pending |= target_mask; hscb->control |= MK_MESSAGE; - scb->flags |= SCB_MSGOUT_WDTR_16BIT; + scb->flags |= SCB_MSGOUT_WDTR; } if ( p->needsdtr_copy & target_mask ) { @@ -3973,12 +4772,26 @@ } } } - + else + scb->flags &= ~SCB_MSGOUT_BITS; +#endif /* AIC7XXX_FAKE_NEGOTIATION_CMDS */ scb->flags |= SCB_SENSE; /* * Ensure the target is busy since this will be an * an untagged request. */ +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + { + if (scb->flags & SCB_MSGOUT_BITS) + printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no, + CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ? + "SDTR" : "WDTR"); + else + printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no, + CTL_OF_SCB(scb)); + } +#endif aic7xxx_busy_target(p, scb); aic_outb(p, SEND_SENSE, RETURN_1); aic7xxx_error(cmd) = DID_OK; @@ -4003,7 +4816,7 @@ */ aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL, 0, TRUE, - &p->delayed_scbs[scratch_offset]); + &p->delayed_scbs[tindex]); next_scbp = p->waiting_scbs.head; while ( next_scbp != NULL ) { @@ -4013,7 +4826,7 @@ SCB_LIST_NULL) ) { scbq_remove(&p->waiting_scbs, prev_scbp); - scbq_insert_tail(&p->delayed_scbs[scratch_offset], + scbq_insert_tail(&p->delayed_scbs[tindex], prev_scbp); } } @@ -4033,15 +4846,15 @@ { if (next_scbp->flags & SCB_WAITINGQ) { - p->dev_active_cmds[scratch_offset]++; + p->dev_active_cmds[tindex]++; p->activescbs--; - scbq_remove(&p->delayed_scbs[scratch_offset], next_scbp); + scbq_remove(&p->delayed_scbs[tindex], next_scbp); scbq_remove(&p->waiting_scbs, next_scbp); } - scbq_insert_head(&p->delayed_scbs[scratch_offset], + scbq_insert_head(&p->delayed_scbs[tindex], next_scbp); next_scbp->flags |= SCB_WAITINGQ; - p->dev_active_cmds[scratch_offset]--; + p->dev_active_cmds[tindex]--; p->activescbs--; next_hscb = aic_inb(p, SCB_NEXT); aic_outb(p, 0, SCB_CONTROL); @@ -4074,65 +4887,67 @@ aic_outb(p, active_hscb, SCBPTR); if (scb->flags & SCB_WAITINGQ) { - scbq_remove(&p->delayed_scbs[scratch_offset], scb); + scbq_remove(&p->delayed_scbs[tindex], scb); scbq_remove(&p->waiting_scbs, scb); - p->dev_active_cmds[scratch_offset]++; + p->dev_active_cmds[tindex]++; p->activescbs++; } - scbq_insert_head(&p->delayed_scbs[scratch_offset], scb); - p->dev_active_cmds[scratch_offset]--; + scbq_insert_head(&p->delayed_scbs[tindex], scb); + p->dev_active_cmds[tindex]--; p->activescbs--; scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY; - if (p->dev_timer[scratch_offset].expires == 0) + if (p->dev_timer[tindex].expires == 0) { - if ( p->dev_active_cmds[scratch_offset] ) + if ( p->dev_active_cmds[tindex] ) { - p->dev_timer[scratch_offset].expires = jiffies + (HZ * 2); - add_timer(&p->dev_timer[scratch_offset]); + p->dev_timer[tindex].expires = jiffies + (HZ * 2); + add_timer(&p->dev_timer[tindex]); } else { - p->dev_timer[scratch_offset].expires = jiffies + (HZ / 2); - add_timer(&p->dev_timer[scratch_offset]); + p->dev_timer[tindex].expires = jiffies + (HZ / 2); + add_timer(&p->dev_timer[tindex]); } } - if (aic7xxx_verbose & VERBOSE_QUEUE_FULL) +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose & VERBOSE_MINOR_ERROR) { if (queue_flag) printk(INFO_LEAD "Queue full received; queue depth %d, " "active %d\n", p->host_no, CTL_OF_SCB(scb), - p->dev_max_queue_depth[scratch_offset], - p->dev_active_cmds[scratch_offset]); + p->dev_max_queue_depth[tindex], + p->dev_active_cmds[tindex]); else printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb)); } +#endif if (queue_flag) { - p->dev_temp_queue_depth[scratch_offset] = - p->dev_active_cmds[scratch_offset]; - if ( p->dev_last_queue_full[scratch_offset] != - p->dev_active_cmds[scratch_offset] ) + p->dev_temp_queue_depth[tindex] = + p->dev_active_cmds[tindex]; + if ( p->dev_last_queue_full[tindex] != + p->dev_active_cmds[tindex] ) { - p->dev_last_queue_full[scratch_offset] = - p->dev_active_cmds[scratch_offset]; - p->dev_last_queue_full_count[scratch_offset] = 0; + p->dev_last_queue_full[tindex] = + p->dev_active_cmds[tindex]; + p->dev_last_queue_full_count[tindex] = 0; } else { - p->dev_last_queue_full_count[scratch_offset]++; + p->dev_last_queue_full_count[tindex]++; } - if ( (p->dev_last_queue_full_count[scratch_offset] > 14) && - (p->dev_active_cmds[scratch_offset] > 4) ) + if ( (p->dev_last_queue_full_count[tindex] > 14) && + (p->dev_active_cmds[tindex] > 4) ) { - if (aic7xxx_verbose & VERBOSE_NEGOTIATION) + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no, - CTL_OF_SCB(scb), p->dev_active_cmds[scratch_offset]); - p->dev_max_queue_depth[scratch_offset] = - p->dev_active_cmds[scratch_offset]; - p->dev_last_queue_full[scratch_offset] = 0; - p->dev_last_queue_full_count[scratch_offset] = 0; + CTL_OF_SCB(scb), p->dev_active_cmds[tindex]); + p->dev_max_queue_depth[tindex] = + p->dev_active_cmds[tindex]; + p->dev_last_queue_full[tindex] = 0; + p->dev_last_queue_full_count[tindex] = 0; } } break; @@ -4166,6 +4981,15 @@ * this target. */ + if ( !(scb->flags & SCB_DEVICE_RESET) && + (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG) && + (scb->hscb->control & TAG_ENB) ) + { + p->msg_buf[p->msg_index++] = scb->tag_action; + p->msg_buf[p->msg_index++] = scb->hscb->tag; + p->msg_len += 2; + } + if (scb->flags & SCB_DEVICE_RESET) { p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET; @@ -4178,12 +5002,6 @@ { if (scb->tag_action) { - if (msg_out == MSG_IDENTIFYFLAG) - { - p->msg_buf[p->msg_index++] = scb->tag_action; - p->msg_buf[p->msg_index++] = scb->hscb->tag; - p->msg_len += 2; - } p->msg_buf[p->msg_index++] = MSG_ABORT_TAG; } else @@ -4197,23 +5015,67 @@ } else if (scb->flags & SCB_MSGOUT_WDTR) { - aic7xxx_construct_wdtr(p, (scb->flags & SCB_WDTR_16BIT)); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Sending WDTR message.\n", p->host_no, + CTL_OF_SCB(scb)); +#endif + aic7xxx_construct_wdtr(p, + p->transinfo[TARGET_INDEX(scb->cmd)].goal_width); } else if (scb->flags & SCB_MSGOUT_SDTR) { - unsigned char period, offset; - + unsigned int max_sync, period; /* - * Pull the user defined setting from scratch RAM. + * We need to set an accurate goal_offset instead of + * the ridiculously high one we default to. We should + * now know if we are wide. Plus, the WDTR code will + * set our goal_offset for us as well. */ - period = p->syncinfo[scratch_offset].period; - offset = p->syncinfo[scratch_offset].offset; - if ( (p->needsdtr_copy & target_mask) == 0) + if (p->transinfo[tindex].goal_offset) + { + if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; + else if (p->transinfo[tindex].cur_width == MSG_EXT_WDTR_BUS_16_BIT) + p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + else + p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + } + /* + * Now that the device is selected, use the bits in SBLKCTL and + * SSTAT2 to determine the max sync rate for this device. + */ + if (p->features & AHC_ULTRA2) + { + if ( (aic_inb(p, SBLKCTL) & ENAB40) && + !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) + { + max_sync = AHC_SYNCRATE_ULTRA2; + } + else + { + max_sync = AHC_SYNCRATE_ULTRA; + } + } + else if (p->features & AHC_ULTRA) { - period = 0; - offset = 0; + max_sync = AHC_SYNCRATE_ULTRA; } - aic7xxx_construct_sdtr(p, period, offset); + else + { + max_sync = AHC_SYNCRATE_FAST; + } + period = p->transinfo[tindex].goal_period; + aic7xxx_find_syncrate(p, &period, max_sync); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no, + CTL_OF_SCB(scb), + p->transinfo[tindex].goal_period, + p->transinfo[tindex].goal_offset); +#endif + aic7xxx_construct_sdtr(p, period, + p->transinfo[tindex].goal_offset); } else { @@ -4233,7 +5095,7 @@ * have this problem since they continually interrupt the kernel * until we take care of the situation. */ - aic_outb(p, CLRSEQINT, CLRINT); + scb->flags |= SCB_MSGOUT_SENT; p->msg_index = 0; p->msg_type = MSG_TYPE_INITIATOR_MSGOUT; p->flags |= AHC_HANDLING_REQINITS; @@ -4282,6 +5144,7 @@ } break; +#if AIC7XXX_NOT_YET case TRACEPOINT: { printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel, @@ -4296,7 +5159,6 @@ } break; -#if AIC7XXX_NOT_YET /* XXX Fill these in later */ case MSG_BUFFER_BUSY: printk("aic7xxx: Message buffer busy.\n"); @@ -4316,7 +5178,6 @@ /* * Clear the sequencer interrupt and unpause the sequencer. */ - aic_outb(p, CLRSEQINT, CLRINT); unpause_sequencer(p, /* unpause always */ TRUE); } @@ -4331,14 +5192,18 @@ static int aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - int reject, done; - unsigned char target_scratch, scratch_offset; + int reject, reply, done; + unsigned char target_scsirate, tindex; unsigned short target_mask; + unsigned char target, channel, lun; - reject = done = FALSE; - scratch_offset = TARGET_INDEX(scb->cmd); - target_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset); - target_mask = (0x01 << scratch_offset); + target = scb->cmd->target; + channel = scb->cmd->channel; + lun = scb->cmd->lun; + reply = reject = done = FALSE; + tindex = TARGET_INDEX(scb->cmd); + target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex); + target_mask = (0x01 << tindex); /* * Parse as much of the message as is availible, @@ -4364,9 +5229,10 @@ { case MSG_EXT_SDTR: { - unsigned char period, response_period, offset; - unsigned char max_offset, saved_offset, rate; - + unsigned int period, offset; + unsigned char maxsync, saved_offset; + struct aic7xxx_syncrate *syncrate; + if (p->msg_buf[1] != MSG_EXT_SDTR_LEN) { reject = TRUE; @@ -4379,51 +5245,114 @@ } period = p->msg_buf[3]; - saved_offset = p->msg_buf[4]; + saved_offset = offset = p->msg_buf[4]; - if (target_scratch & WIDEXFER) + if (p->features & AHC_ULTRA2) + { + if ( (aic_inb(p, SBLKCTL) & ENAB40) && + !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) + { + maxsync = AHC_SYNCRATE_ULTRA2; + } + else + { + maxsync = AHC_SYNCRATE_ULTRA; + } + } + else if (p->features & AHC_ULTRA) { - max_offset = MAX_OFFSET_16BIT; + maxsync = AHC_SYNCRATE_ULTRA; } else { - max_offset = MAX_OFFSET_8BIT; + maxsync = AHC_SYNCRATE_FAST; } - offset = MIN(saved_offset, max_offset); - response_period = aic7xxx_scsirate(p, &rate, &period, - &offset, scb->cmd->target, scb->cmd->channel, /* set */ TRUE); - /* Preserve the WideXfer flag */ - target_scratch = rate | (target_scratch & WIDEXFER); - - /* - * Update the TARGET_SCRATCH, the SCSIRATE, and our syncinfo - * areas. - */ - aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset); - aic_outb(p, target_scratch, SCSIRATE); - p->syncinfo[scratch_offset].period = response_period; - p->syncinfo[scratch_offset].offset = offset; + +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + { + printk(INFO_LEAD "Finished receipt of SDTR, parsing %d/%d\n", + p->host_no, CTL_OF_SCB(scb), period, offset); + syncrate = aic7xxx_find_syncrate(p, &period, maxsync); + printk(INFO_LEAD "After find_syncrate() %d/%d\n", + p->host_no, CTL_OF_SCB(scb), period, offset); + aic7xxx_validate_offset(p, syncrate, &offset, + target_scsirate & WIDEXFER); + printk(INFO_LEAD "After validate_offset() %d/%d\n", + p->host_no, CTL_OF_SCB(scb), period, offset); + aic7xxx_set_syncrate(p, syncrate, target, channel, period, + offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + printk(INFO_LEAD "Final values of Period/Offset as set: %d/%d\n", + p->host_no, CTL_OF_SCB(scb), period, offset); + } + else + { + syncrate = aic7xxx_find_syncrate(p, &period, maxsync); + aic7xxx_validate_offset(p, syncrate, &offset, + target_scsirate & WIDEXFER); + aic7xxx_set_syncrate(p, syncrate, target, channel, period, + offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + } +#else + syncrate = aic7xxx_find_syncrate(p, &period, maxsync); + aic7xxx_validate_offset(p, syncrate, &offset, + target_scsirate & WIDEXFER); + aic7xxx_set_syncrate(p, syncrate, target, channel, period, + offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR); +#endif + if (offset == 0) + { + /* + * Uhh ohh, things fell through to async....update the goal + * items and the needsdtr_copy to reflect this... + */ + aic7xxx_set_syncrate(p, syncrate, target, channel, period, + offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE); + p->needsdtr_copy &= ~target_mask; + } /* * Did we start this, if not, or if we went to low and had to * go async, then send an SDTR back to the target */ p->needsdtr &= ~target_mask; - if (scb->flags & SCB_MSGOUT_SDTR) + p->sdtr_pending &= ~target_mask; + if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) == + (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) && + (offset == saved_offset) ) { - if (saved_offset != offset) - { - p->needsdtr_copy &= ~target_mask; - reject = TRUE; - } - scb->flags &= ~SCB_MSGOUT_SDTR; - p->sdtr_pending &= ~target_mask; + scb->flags &= ~SCB_MSGOUT_BITS; } else { + /* + * Send a reply SDTR back. Even if we sent the first one, it + * is valid to send another one out immediately to re-negotiate + * things, and a few devices don't like getting rejects after + * we already sent them one SDTR. Just send an SDTR for async + * this time if need be (or for the correct params if we didn't + * start all of this). If this is a Reject Reply type message, + * then we've put the async settings into the goal area for + * future reference (when we get the AWAITING_MSG interrupt). + * If this is a case where we are responding to the target's + * initiated SDTR, then leave our own goal and user values in + * place (unless the device hasn't been scanned yet, in which + * case, put the user values into the goal values so we don't + * send out an Async message). + */ + if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) ) + { + p->transinfo[tindex].goal_width = + p->transinfo[tindex].user_width; + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->needwdtr_copy |= target_mask; + p->needsdtr_copy |= target_mask; + } scb->flags &= ~SCB_MSGOUT_BITS; scb->flags |= SCB_MSGOUT_SDTR; - p->sdtr_pending |= target_mask; aic_outb(p, HOST_MSG, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); } @@ -4446,112 +5375,111 @@ } bus_width = p->msg_buf[3]; - p->needwdtr &= ~target_mask; - if (scb->flags & SCB_MSGOUT_WDTR) + if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) == + (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) ) { switch(bus_width) { default: { reject = TRUE; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) + if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && + ((p->dev_flags[tindex] & DEVICE_PRINT_WDTR) || + (aic7xxx_verbose > 0xffff)) ) { printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n", p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; + p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR; } } /* We fall through on purpose */ case MSG_EXT_WDTR_BUS_8_BIT: { bus_width = MSG_EXT_WDTR_BUS_8_BIT; p->needwdtr_copy &= ~target_mask; - target_scratch &= 0x7f; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) - { - printk(INFO_LEAD "Using narrow (8 bit) transfers.\n", - p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } break; } case MSG_EXT_WDTR_BUS_16_BIT: { - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) - { - printk(INFO_LEAD "Using wide (16 bit) transfers.\n", - p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } - target_scratch |= WIDEXFER; break; } } - scb->flags &= ~SCB_MSGOUT_WDTR_16BIT; + scb->flags &= ~SCB_MSGOUT_BITS; p->wdtr_pending &= ~target_mask; - /* - * By virtue of the SCSI spec, a WDTR message negates any existing - * SDTR negotiations. So, even if needsdtr isn't marked for this - * device, we still have to do a new SDTR message if the device - * supports SDTR at all. Therefore, we check needsdtr_copy instead - * of needstr. - */ - if ( (p->needsdtr_copy & target_mask) && - !(p->sdtr_pending & target_mask)) - { - p->needsdtr |= target_mask; - } + p->needwdtr &= ~target_mask; } else { scb->flags &= ~SCB_MSGOUT_BITS; + scb->flags |= SCB_MSGOUT_WDTR; + reply = TRUE; + if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) ) + { + /* + * Well, we now know the WDTR and SYNC caps of this device since + * it contacted us first, mark it as such and copy the user stuff + * over to the goal stuff. + */ + p->transinfo[tindex].goal_width = + p->transinfo[tindex].user_width; + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->needwdtr_copy |= target_mask; + p->needsdtr_copy |= target_mask; + } switch(bus_width) { default: { - if (p->type & AHC_WIDE) + if ( (p->features & AHC_WIDE) && + (p->transinfo[tindex].goal_width == + MSG_EXT_WDTR_BUS_16_BIT) ) { bus_width = MSG_EXT_WDTR_BUS_16_BIT; - p->needwdtr_copy |= target_mask; - scb->flags |= SCB_MSGOUT_WDTR_16BIT; - target_scratch |= WIDEXFER; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) - { - printk(INFO_LEAD "Using wide (16 bit) transfers.\n", - p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } break; } } /* Fall through if we aren't a wide card */ case MSG_EXT_WDTR_BUS_8_BIT: { - bus_width = MSG_EXT_WDTR_BUS_8_BIT; p->needwdtr_copy &= ~target_mask; - scb->flags |= SCB_MSGOUT_WDTR_8BIT; - target_scratch &= 0x7f; - if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) && - (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) ) - { - printk(INFO_LEAD "Using narrow (8 bit) transfers.\n", - p->host_no, CTL_OF_SCB(scb)); - p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR; - } + bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } } + p->needwdtr &= ~target_mask; + p->wdtr_pending &= ~target_mask; aic_outb(p, HOST_MSG, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); - p->wdtr_pending |= target_mask; } - aic_outb(p, target_scratch, SCSIRATE); - aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset); - p->syncinfo[scratch_offset].offset = - (bus_width == MSG_EXT_WDTR_BUS_8_BIT) ? - MAX_OFFSET_8BIT : MAX_OFFSET_16BIT; + aic7xxx_set_width(p, target, channel, lun, bus_width, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + + /* + * By virtue of the SCSI spec, a WDTR message negates any existing + * SDTR negotiations. So, even if needsdtr isn't marked for this + * device, we still have to do a new SDTR message if the device + * supports SDTR at all. Therefore, we check needsdtr_copy instead + * of needstr. + */ + aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, + AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE); + if ( (p->needsdtr_copy & target_mask) && + !(p->sdtr_pending & target_mask)) + { + p->needsdtr |= target_mask; + if ( !reject && !reply ) + { + scb->flags &= ~SCB_MSGOUT_WDTR; + if (p->transinfo[tindex].goal_period) + { + p->sdtr_pending |= target_mask; + scb->flags |= SCB_MSGOUT_SDTR; + aic_outb(p, HOST_MSG, MSG_OUT); + aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); + } + } + } done = TRUE; break; } @@ -4567,10 +5495,12 @@ { aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT); aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO); + done = TRUE; } return(done); } + /*+F************************************************************************* * Function: * aic7xxx_handle_reqinit @@ -4584,7 +5514,7 @@ { unsigned char lastbyte; unsigned char phasemis; - int done; + int done = FALSE; switch(p->msg_type) { @@ -4614,10 +5544,20 @@ { aic_outb(p, p->msg_buf[p->msg_index], SINDEX); aic_outb(p, 0, RETURN_1); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Completed sending of REQINIT message.\n", + p->host_no, CTL_OF_SCB(scb)); +#endif } else { aic_outb(p, MSGOUT_PHASEMIS, RETURN_1); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n", + p->host_no, CTL_OF_SCB(scb)); +#endif } unpause_sequencer(p, TRUE); } @@ -4651,12 +5591,23 @@ } if (phasemis || done) { +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + { + if (phasemis) + printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n", + p->host_no, CTL_OF_SCB(scb)); + else + printk(INFO_LEAD "Completed receipt of REQINIT message.\n", + p->host_no, CTL_OF_SCB(scb)); + } +#endif /* Time to end our message session */ p->msg_len = 0; p->msg_type = MSG_TYPE_NONE; - p->flags &= ~AHC_HANDLING_REQINITS; aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1); aic_outb(p, CLRSCSIINT, CLRINT); + p->flags &= ~AHC_HANDLING_REQINITS; unpause_sequencer(p, TRUE); } break; @@ -4700,30 +5651,14 @@ } - if ( (p->flags & AHC_HANDLING_REQINITS) && (status & REQINIT) ) - { - if (scb) - { - aic7xxx_handle_reqinit(p, scb); - } - else - { - p->flags &= ~AHC_HANDLING_REQINITS; - aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1); - aic_outb(p, CLRREQINIT, CLRSINT1); - aic_outb(p, CLRSCSIINT, CLRINT); - p->msg_type = MSG_TYPE_NONE; - p->msg_index = 0; - p->msg_len = 0; - } - return; - } - if ((status & SCSIRSTI) != 0) { int channel; - channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) + channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + else + channel = 0; if (aic7xxx_verbose & VERBOSE_RESET) printk(WARN_LEAD "Someone else reset the channel!!\n", @@ -4746,10 +5681,16 @@ unsigned char lastphase = aic_inb(p, LASTPHASE); unsigned char saved_tcl = aic_inb(p, SAVED_TCL); unsigned char target = (saved_tcl >> 4) & 0x0F; - int channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + int channel; int printerror = TRUE; - aic_outb(p, 0, SCSISEQ); + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) + channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3; + else + channel = 0; + + aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), + SCSISEQ); if (lastphase == P_MESGOUT) { unsigned char message; @@ -4791,20 +5732,16 @@ aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag); aic7xxx_run_done_queue(p, FALSE); } - else - { /* Since we don't really know what happened here, we'll wait */ - /* for the commands to timeout and get aborted if need be */ - aic7xxx_add_curscb_to_free_list(p); - } printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, " "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase, (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0)); scb = NULL; } + aic_outb(p, MSG_NOOP, MSG_OUT); aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT), SIMODE1); p->flags &= ~AHC_HANDLING_REQINITS; - aic_outb(p, CLRBUSFREE | CLRREQINIT, CLRSINT1); + aic_outb(p, CLRBUSFREE, CLRSINT1); aic_outb(p, CLRSCSIINT, CLRINT); restart_sequencer(p); unpause_sequencer(p, TRUE); @@ -4816,17 +5753,26 @@ Scsi_Cmnd *cmd; scbptr = aic_inb(p, WAITING_SCBH); - if (scbptr >= p->scb_data->maxhscbs) - { - scb_index = SCB_LIST_NULL; - printk(WARN_LEAD "Bad scbptr %d during SELTO.\n", - p->host_no, -1, -1, -1, scbptr); - } - else + if (scbptr > p->scb_data->maxhscbs) { - aic_outb(p, scbptr, SCBPTR); - scb_index = aic_inb(p, SCB_TAG); + /* + * I'm still trying to track down exactly how this happens, but until + * I find it, this code will make sure we aren't passing bogus values + * into the SCBPTR register, even if that register will just wrap + * things around, we still don't like having out of range variables. + * + * NOTE: Don't check the aic7xxx_verbose variable, I want this message + * to always be displayed. + */ + printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n", + p->host_no, -1, -1, -1, scbptr); + if (p->scb_data->maxhscbs > 4) + scbptr &= (p->scb_data->maxhscbs - 1); + else + scbptr &= 0x03; } + aic_outb(p, scbptr, SCBPTR); + scb_index = aic_inb(p, SCB_TAG); scb = NULL; if (scb_index < p->scb_data->numscbs) @@ -4845,6 +5791,8 @@ "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ), aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8), aic_inb(p, SSTAT0), aic_inb(p, SSTAT1)); + if (aic7xxx_panic_on_abort) + aic7xxx_panic_abort(p, NULL); } else { @@ -4881,6 +5829,10 @@ * What we need to do then is to let the command timeout again so * we get a reset since this abort just failed. */ +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb)); +#endif if (p->flags & SCB_QUEUED_ABORT) { cmd->result = 0; @@ -4892,9 +5844,10 @@ * Restarting the sequencer will stop the selection and make sure devices * are allowed to reselect in. */ - aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1); + aic_outb(p, 0, SCSISEQ); + aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1); p->flags &= ~AHC_HANDLING_REQINITS; - aic_outb(p, CLRSELTIMEO | CLRBUSFREE | CLRREQINIT, CLRSINT1); + aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1); aic_outb(p, CLRSCSIINT, CLRINT); restart_sequencer(p); unpause_sequencer(p, TRUE); @@ -4959,7 +5912,7 @@ * A parity error has occurred during a data * transfer phase. Flag it and continue. */ - printk(WARN_LEAD "Parity error during phase %s.\n", + printk(WARN_LEAD "Parity error during %s phase.\n", p->host_no, CTL_OF_SCB(scb), phase); /* @@ -4977,6 +5930,17 @@ aic_outb(p, CLRSCSIINT, CLRINT); unpause_sequencer(p, /* unpause_always */ TRUE); } + else if ( (status & REQINIT) && + (p->flags & AHC_HANDLING_REQINITS) ) + { +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no, + CTL_OF_SCB(scb), aic_inb(p, SSTAT1)); +#endif + aic7xxx_handle_reqinit(p, scb); + return; + } else { /* @@ -4997,83 +5961,168 @@ } } -#ifdef CONFIG_PCI +#ifdef AIC7XXX_VERBOSE_DEBUGGING +static void +aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer) +{ + unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp; + int i, bogus, lost; + static unsigned char scb_status[AIC7XXX_MAXSCB]; + +#define SCB_NO_LIST 0 +#define SCB_FREE_LIST 1 +#define SCB_WAITING_LIST 2 +#define SCB_DISCONNECTED_LIST 4 +#define SCB_CURRENTLY_ACTIVE 8 + + /* + * Note, these checks will fail on a regular basis once the machine moves + * beyond the bus scan phase. The problem is race conditions concerning + * the scbs and where they are linked in. When you have 30 or so commands + * outstanding on the bus, and run this twice with every interrupt, the + * chances get pretty good that you'll catch the sequencer with an SCB + * only partially linked in. Therefore, once we pass the scan phase + * of the bus, we really should disable this function. + */ + bogus = FALSE; + memset(&scb_status[0], 0, sizeof(scb_status)); + pause_sequencer(p); + saved_scbptr = aic_inb(p, SCBPTR); + if (saved_scbptr >= p->scb_data->maxhscbs) + { + printk("Bogus SCBPTR %d\n", saved_scbptr); + bogus = TRUE; + } + scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE; + free_scbh = aic_inb(p, FREE_SCBH); + if ( (free_scbh != SCB_LIST_NULL) && + (free_scbh >= p->scb_data->maxhscbs) ) + { + printk("Bogus FREE_SCBH %d\n", free_scbh); + bogus = TRUE; + } + else + { + temp = free_scbh; + while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) ) + { + if(scb_status[temp] & 0x07) + { + printk("HSCB %d on multiple lists, status 0x%02x", temp, + scb_status[temp] | SCB_FREE_LIST); + bogus = TRUE; + } + scb_status[temp] |= SCB_FREE_LIST; + aic_outb(p, temp, SCBPTR); + temp = aic_inb(p, SCB_NEXT); + } + } -#define DPE 0x80 -#define SSE 0x40 -#define RMA 0x20 -#define RTA 0x10 -#define STA 0x08 -#define DPR 0x01 + dis_scbh = aic_inb(p, DISCONNECTED_SCBH); + if ( (dis_scbh != SCB_LIST_NULL) && + (dis_scbh >= p->scb_data->maxhscbs) ) + { + printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh); + bogus = TRUE; + } + else + { + temp = dis_scbh; + while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) ) + { + if(scb_status[temp] & 0x07) + { + printk("HSCB %d on multiple lists, status 0x%02x", temp, + scb_status[temp] | SCB_DISCONNECTED_LIST); + bogus = TRUE; + } + scb_status[temp] |= SCB_DISCONNECTED_LIST; + aic_outb(p, temp, SCBPTR); + temp = aic_inb(p, SCB_NEXT); + } + } + + wait_scbh = aic_inb(p, WAITING_SCBH); + if ( (wait_scbh != SCB_LIST_NULL) && + (wait_scbh >= p->scb_data->maxhscbs) ) + { + printk("Bogus WAITING_SCBH %d\n", wait_scbh); + bogus = TRUE; + } + else + { + temp = wait_scbh; + while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) ) + { + if(scb_status[temp] & 0x07) + { + printk("HSCB %d on multiple lists, status 0x%02x", temp, + scb_status[temp] | SCB_WAITING_LIST); + bogus = TRUE; + } + scb_status[temp] |= SCB_WAITING_LIST; + aic_outb(p, temp, SCBPTR); + temp = aic_inb(p, SCB_NEXT); + } + } + + lost=0; + for(i=0; i < p->scb_data->maxhscbs; i++) + { + aic_outb(p, i, SCBPTR); + temp = aic_inb(p, SCB_NEXT); + if ( ((temp != SCB_LIST_NULL) && + (temp >= p->scb_data->maxhscbs)) ) + { + printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp); + bogus = TRUE; + } + if ( temp == i ) + { + printk("HSCB %d bad, SCB_NEXT points to self.\n", i); + bogus = TRUE; + } + temp = aic_inb(p, SCB_PREV); + if ((temp != SCB_LIST_NULL) && + (temp >= p->scb_data->maxhscbs)) + { + printk("HSCB %d bad, SCB_PREV invalid(%d).\n", i, temp); + bogus = TRUE; + } + if (scb_status[i] == 0) + lost++; + if (lost > 1) + { + printk("Too many lost scbs.\n"); + bogus=TRUE; + } + } + aic_outb(p, saved_scbptr, SCBPTR); + unpause_sequencer(p, FALSE); + if (bogus) + { + printk("Bogus parameters found in card SCB array structures.\n"); + printk("%s\n", buffer); + aic7xxx_panic_abort(p, NULL); + } + return; +} +#endif /*+F************************************************************************* * Function: - * aic7xxx_pci_intr + * aic7xxx_isr * * Description: - * Check the scsi card for PCI errors and clear the interrupt - * - * NOTE: If you don't have this function and a 2940 card encounters - * a PCI error condition, the machine will end up locked as the - * interrupt handler gets slammed with non-stop PCI error interrupts + * SCSI controller interrupt handler. *-F*************************************************************************/ static void -aic7xxx_pci_intr(struct aic7xxx_host *p) +aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) { - unsigned char status1; + struct aic7xxx_host *p; + unsigned char intstat; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - pci_read_config_byte(p->pdev, PCI_STATUS, &status1); -#else - pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, - PCI_STATUS, &status1); -#endif - - if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Data Parity Error during PCI address or PCI write" - "phase.\n", p->host_no, -1, -1, -1); - if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Signal System Error Detected\n", p->host_no, - -1, -1, -1); - if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no, - -1, -1, -1); - if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no, - -1, -1, -1); - if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no, - -1, -1, -1); - if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) - printk(WARN_LEAD "Data Parity Error has been reported via PCI pin " - "PERR#\n", p->host_no, -1, -1, -1); - -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - pci_write_config_byte(p->pdev, PCI_STATUS, status1); -#else - pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, - PCI_STATUS, status1); -#endif - if (status1 & (DPR|RMA|RTA)) - aic_outb(p, CLRPARERR, CLRINT); - -} -#endif - -/*+F************************************************************************* - * Function: - * aic7xxx_isr - * - * Description: - * SCSI controller interrupt handler. - *-F*************************************************************************/ -static void -aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct aic7xxx_host *p; - unsigned char intstat; - - p = (struct aic7xxx_host *)dev_id; + p = (struct aic7xxx_host *)dev_id; /* * Just a few sanity checks. Make sure that we have an int pending. @@ -5083,7 +6132,8 @@ if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND)) { #ifdef CONFIG_PCI - if ((p->type & AHC_AIC78x0) && (p->spurious_int > 500)) + if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) && + !(p->flags & AHC_HANDLING_REQINITS) ) { if ( aic_inb(p, ERROR) & PCIERRSTAT ) { @@ -5091,7 +6141,7 @@ } p->spurious_int = 0; } - else + else if ( !(p->flags & AHC_HANDLING_REQINITS) ) { p->spurious_int++; } @@ -5106,6 +6156,12 @@ */ p->isr_count++; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) && + (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) ) + aic7xxx_check_scbs(p, "Bogus settings at start of interrupt."); +#endif + /* * Handle all the interrupt sources - especially for SCSI * interrupts, we won't get a second chance at them. @@ -5116,6 +6172,11 @@ Scsi_Cmnd *cmd; unsigned char scb_index; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if(aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1); +#endif + /* * Clear interrupt status before running the completion loop. * This eliminates a race condition whereby a command could @@ -5169,6 +6230,14 @@ SCB_QUEUED_ABORT); unpause_sequencer(p, FALSE); } + else if (scb->flags & SCB_ABORT) + { + /* + * We started to abort this, but it completed on us, let it + * through as successful + */ + scb->flags &= ~(SCB_ABORT|SCB_RESET); + } switch (status_byte(scb->hscb->target_status)) { case QUEUE_FULL: @@ -5209,16 +6278,35 @@ printk(KERN_ERR " %s\n", hard_error[i].errmesg); } } - printk(KERN_ERR "(scsi%d) LINE=%d\n", p->host_no, + printk(KERN_ERR "(scsi%d) SEQADDR=0x%x\n", p->host_no, (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0))); - aic7xxx_reset_channel(p, 0, TRUE); - if ( p->type & AHC_TWIN ) + if (aic7xxx_panic_on_abort) + aic7xxx_panic_abort(p, NULL); +#ifdef CONFIG_PCI + if (errno & PCIERRSTAT) + aic7xxx_pci_intr(p); +#endif + if (errno & (SQPARERR | ILLOPCODE | ILLSADDR)) { - aic7xxx_reset_channel(p, 1, TRUE); - restart_sequencer(p); + sti(); + panic("aic7xxx: unrecoverable BRKADRINT.\n"); } - aic7xxx_run_done_queue(p, FALSE); - aic_outb(p, CLRBRKADRINT, CLRINT); + if (errno & ILLHADDR) + { + printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first " + "pausing controller!\n", p->host_no); + } +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (errno & DPARERR) + { + if (aic_inb(p, DMAPARAMS) & DIRECTION) + printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no); + else + printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no); + } +#endif + aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT); + unpause_sequencer(p, FALSE); } if (intstat & SEQINT) @@ -5230,6 +6318,13 @@ { aic7xxx_handle_scsiint(p, intstat); } + +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) && + (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) ) + aic7xxx_check_scbs(p, "Bogus settings at end of interrupt."); +#endif + } /*+F************************************************************************* @@ -5246,49 +6341,34 @@ { unsigned long cpu_flags; struct aic7xxx_host *p; - static unsigned int re_entry_counter = 0; p = (struct aic7xxx_host *)dev_id; if(!p) return; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, cpu_flags); if(test_and_set_bit(AHC_IN_ISR_BIT, &p->flags)) { - if(re_entry_counter++ > 100000UL) - { - /* - * Hmmm...we seem to be looping here. This usually means that our - * interrupt routine got killed by a NULL pointer deref. Panic. - */ - sti(); - panic("aic7xxx: The interrupt routine appears to have seg faulted.\n"); - } return; } - re_entry_counter = 0; - spin_lock_irqsave(&io_request_lock, cpu_flags); - aic7xxx_isr(irq, dev_id, regs); + do + { + aic7xxx_isr(irq, dev_id, regs); + } while ( (aic_inb(p, INTSTAT) & INT_PEND) ); aic7xxx_done_cmds_complete(p); aic7xxx_run_waiting_queues(p); - spin_unlock_irqrestore(&io_request_lock, cpu_flags); clear_bit(AHC_IN_ISR_BIT, &p->flags); + spin_unlock_irqrestore(&io_request_lock, cpu_flags); #else if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags)) { - if(re_entry_counter++ > 100000UL) - { - /* - * Hmmm...we seem to be looping here. This usually means that our - * interrupt routine got killed by a NULL pointer deref. Panic. - */ - sti(); - panic("aic7xxx: The interrupt routine appears to have seg faulted.\n"); - } return; } - re_entry_counter = 0; DRIVER_LOCK - aic7xxx_isr(irq, dev_id, regs); + do + { + aic7xxx_isr(irq, dev_id, regs); + } while ( (aic_inb(p, INTSTAT) & INT_PEND) ); DRIVER_UNLOCK aic7xxx_done_cmds_complete(p); aic7xxx_run_waiting_queues(p); @@ -5341,7 +6421,7 @@ if (!(p->discenable & target_mask)) { - if (aic7xxx_verbose & VERBOSE_QUEUE) + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) printk(INFO_LEAD "Disconnection disabled, unable to " "enable tagged queueing.\n", p->host_no, device->channel, device->id, device->lun); @@ -5381,7 +6461,7 @@ } if ((device->tagged_queue == 0) && tag_enabled) { - if (aic7xxx_verbose & VERBOSE_QUEUE) + if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n", p->host_no, device->channel, device->id, @@ -5435,7 +6515,7 @@ * to allocate some when memory is more or less exhausted and we need * the SCB in order to perform a swap operation (possible deadlock) */ - if ( aic7xxx_allocate_scb(p, TRUE) == NULL ) + if ( aic7xxx_allocate_scb(p) == 0 ) return; } } @@ -5468,7 +6548,7 @@ * an unused function. *-F*************************************************************************/ #if defined(__i386__) || defined(__alpha__) -static ahc_type +static int aic7xxx_probe(int slot, int base, ahc_flag_type *flags) { int i; @@ -5477,13 +6557,17 @@ static struct { int n; unsigned char signature[sizeof(buf)]; - ahc_type type; + ahc_chip type; int bios_disabled; } AIC7xxx[] = { - { 4, { 0x04, 0x90, 0x77, 0x71 }, AHC_274, FALSE }, /* host adapter 274x */ - { 4, { 0x04, 0x90, 0x77, 0x70 }, AHC_AIC7770, FALSE }, /* mb 7770 */ - { 4, { 0x04, 0x90, 0x77, 0x56 }, AHC_284, FALSE }, /* 284x BIOS enabled */ - { 4, { 0x04, 0x90, 0x77, 0x57 }, AHC_284, TRUE } /* 284x BIOS disabled */ + { 4, { 0x04, 0x90, 0x77, 0x70 }, + AHC_AIC7770|AHC_EISA, FALSE }, /* mb 7770 */ + { 4, { 0x04, 0x90, 0x77, 0x71 }, + AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */ + { 4, { 0x04, 0x90, 0x77, 0x56 }, + AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */ + { 4, { 0x04, 0x90, 0x77, 0x57 }, + AHC_AIC7770|AHC_VL, TRUE } /* 284x BIOS disabled */ }; /* @@ -5513,7 +6597,7 @@ { *flags |= AHC_BIOS_ENABLED; } - return (AIC7xxx[i].type); + return (i); } printk("aic7xxx: " @@ -5521,7 +6605,7 @@ } } - return (AHC_NONE); + return (-1); } #endif /* (__i386__) || (__alpha__) */ @@ -5678,7 +6762,7 @@ * Description: * Acquires access to the memory port on PCI controllers. *-F*************************************************************************/ -static inline int +static int acquire_seeprom(struct aic7xxx_host *p) { int wait; @@ -5695,7 +6779,7 @@ while ((wait > 0) && ((aic_inb(p, SEECTL) & SEERDY) == 0)) { wait--; - udelay(1000); /* 1 msec */ + mdelay(1); /* 1 msec */ } if ((aic_inb(p, SEECTL) & SEERDY) == 0) { @@ -5712,7 +6796,7 @@ * Description: * Releases access to the memory port on PCI controllers. *-F*************************************************************************/ -static inline void +static void release_seeprom(struct aic7xxx_host *p) { aic_outb(p, 0, SEECTL); @@ -5912,19 +6996,38 @@ * Description: * Writes a value to the BRDCTL register. *-F*************************************************************************/ -static inline void +static void write_brdctl(struct aic7xxx_host *p, unsigned char value) { unsigned char brdctl; - brdctl = BRDCS | BRDSTB; + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) + { + brdctl = BRDSTB; + if (p->flags & AHC_CHNLB) + brdctl |= BRDCS; + } + else if (p->features & AHC_ULTRA2) + brdctl = 0; + else + brdctl = BRDSTB | BRDCS; aic_outb(p, brdctl, BRDCTL); + udelay(1); brdctl |= value; aic_outb(p, brdctl, BRDCTL); - brdctl &= ~BRDSTB; + udelay(1); + if (p->features & AHC_ULTRA2) + brdctl |= BRDSTB_ULTRA2; + else + brdctl &= ~BRDSTB; aic_outb(p, brdctl, BRDCTL); - brdctl &= ~BRDCS; + udelay(1); + if (p->features & AHC_ULTRA2) + brdctl = 0; + else + brdctl &= ~BRDCS; aic_outb(p, brdctl, BRDCTL); + udelay(1); } /*+F************************************************************************* @@ -5934,11 +7037,27 @@ * Description: * Reads the BRDCTL register. *-F*************************************************************************/ -static inline unsigned char +static unsigned char read_brdctl(struct aic7xxx_host *p) { - aic_outb(p, BRDRW | BRDCS, BRDCTL); - return (aic_inb(p, BRDCTL)); + unsigned char brdctl, value; + + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) + { + brdctl = BRDRW; + if (p->flags & AHC_CHNLB) + brdctl |= BRDCS; + } + else if (p->features & AHC_ULTRA2) + brdctl = BRDRW_ULTRA2; + else + brdctl = BRDRW | BRDCS; + aic_outb(p, brdctl, BRDCTL); + udelay(1); + value = aic_inb(p, BRDCTL); + aic_outb(p, 0, BRDCTL); + udelay(1); + return (value); } /*+F************************************************************************* @@ -5955,11 +7074,14 @@ unsigned char brdctl; aic_outb(p, BRDRW | BRDCS, BRDCTL); + udelay(1); aic_outb(p, 0, BRDCTL); + udelay(1); brdctl = aic_inb(p, BRDCTL); + udelay(1); *int_50 = !(brdctl & BRDDAT5); *ext_present = !(brdctl & BRDDAT6); - *eeprom = ( aic_inb(p, SPIOCAP) & EEPROM ) != 0; + *eeprom = (aic_inb(p, SPIOCAP) & EEPROM); } /*+F************************************************************************* @@ -6002,12 +7124,37 @@ brdctl = read_brdctl(p); *ext_present = !(brdctl & BRDDAT6); - *eeprom = (brdctl & BRDDAT7); + *eeprom = !(brdctl & BRDDAT7); /* * We're done, the calling function will release the SEEPROM for us */ - +} + +/*+F************************************************************************* + * Function: + * aic787x_ultra2_term_detect + * + * Description: + * Detect the termination settings present on ultra2 class controllers + * + * NOTE: This functions assumes the SEEPROM will have already been aquired + * prior to invocation of this function. + *-F*************************************************************************/ +static void +aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low, + int *enableSE_high, int *enableLVD_low, + int *enableLVD_high, int *eprom_present) +{ + unsigned char brdctl; + + brdctl = read_brdctl(p); + + *eprom_present = (brdctl & BRDDAT7); + *enableSE_high = (brdctl & BRDDAT6); + *enableSE_low = (brdctl & BRDDAT5); + *enableLVD_high = (brdctl & BRDDAT4); + *enableLVD_low = (brdctl & BRDDAT3); } /*+F************************************************************************* @@ -6019,146 +7166,192 @@ * SEEPROMs available. *-F*************************************************************************/ static void -configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1, - unsigned short adapter_control, unsigned char max_targ) +configure_termination(struct aic7xxx_host *p) { - int internal50_present; + int internal50_present = 0; int internal68_present = 0; int external_present = 0; - int eprom_present; - int high_on; - int low_on; + int eprom_present = 0; + int enableSE_low = 0; + int enableSE_high = 0; + int enableLVD_low = 0; + int enableLVD_high = 0; + unsigned char brddat = 0; + unsigned char max_target = 0; + unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1); if (acquire_seeprom(p)) { - if (adapter_control & CFAUTOTERM) - { - printk(KERN_INFO "aic7xxx: Warning - detected auto-termination on " - "controller:\n"); - printk(KERN_INFO "aic7xxx: <%s> at ", board_names[p->board_name_index]); - switch(p->type & 0x1ff1) - { - case AHC_AIC7770: - case AHC_274: - printk("EISA slot %d\n", p->pci_device_fn); - break; - case AHC_284: - printk("VLB slot %d\n", p->pci_device_fn); - break; - default: - printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn), - PCI_FUNC(p->pci_device_fn)); - break; + if (p->features & (AHC_WIDE|AHC_TWIN)) + max_target = 16; + else + max_target = 8; + aic_outb(p, SEEMS | SEECS, SEECTL); + sxfrctl1 &= ~STPWEN; + if ( (p->adapter_control & CFAUTOTERM) || + (p->features & AHC_ULTRA2) ) + { + if ( (p->adapter_control & CFAUTOTERM) && !(p->features & AHC_ULTRA2) ) + { + printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n", + p->host_no); + printk(KERN_INFO "(scsi%d) Please verify driver detected settings are " + "correct.\n", p->host_no); + printk(KERN_INFO "(scsi%d) If not, then please properly set the device " + "termination\n", p->host_no); + printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting CTRL-A " + "when prompted\n", p->host_no); + printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no); } - printk(KERN_INFO "aic7xxx: Please verify driver detected settings are " - "correct.\n"); - printk(KERN_INFO "aic7xxx: If not, then please properly set the device " - "termination\n"); - printk(KERN_INFO "aic7xxx: in the Adaptec SCSI BIOS by hitting CTRL-A " - "when prompted\n"); - printk(KERN_INFO "aic7xxx: during machine bootup.\n"); /* Configure auto termination. */ - aic_outb(p, SEECS | SEEMS, SEECTL); - if ( (p->type & AHC_AIC7860) == AHC_AIC7860 ) + if (p->features & AHC_ULTRA2) { - aic785x_cable_detect(p, &internal50_present, &external_present, - &eprom_present); + if (aic7xxx_override_term == -1) + aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high, + &enableLVD_low, &enableLVD_high, + &eprom_present); + if (!(p->adapter_control & CFSEAUTOTERM)) + { + enableSE_low = (p->adapter_control & CFSTERM); + enableSE_high = (p->adapter_control & CFWSTERM); + } + if (!(p->adapter_control & CFAUTOTERM)) + { + enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM); + } + internal50_present = 0; + internal68_present = 1; + external_present = 1; } - else + else if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 ) { aic787x_cable_detect(p, &internal50_present, &internal68_present, &external_present, &eprom_present); } - if (max_targ > 8) - { - printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, " - "Ext-68 %s)\n", - internal50_present ? "YES" : "NO", - internal68_present ? "YES" : "NO", - external_present ? "YES" : "NO"); - } else { - printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n", - internal50_present ? "YES" : "NO", - external_present ? "YES" : "NO"); + aic785x_cable_detect(p, &internal50_present, &external_present, + &eprom_present); + } + + if (max_target <= 8) internal68_present = 0; + + if ( !(p->features & AHC_ULTRA2) ) + { + if (max_target > 8) + { + printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, " + "Ext-68 %s)\n", p->host_no, + internal50_present ? "YES" : "NO", + internal68_present ? "YES" : "NO", + external_present ? "YES" : "NO"); + } + else + { + printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n", + p->host_no, + internal50_present ? "YES" : "NO", + external_present ? "YES" : "NO"); + } } if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "aic7xxx: EEPROM %s present.\n", + printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no, eprom_present ? "is" : "is not"); /* * Now set the termination based on what we found. BRDDAT6 * controls wide termination enable. + * Flash Enable = BRDDAT7 + * SE High Term Enable = BRDDAT6 + * SE Low Term Enable = BRDDAT5 (7890) + * LVD High Term Enable = BRDDAT4 (7890) */ - high_on = FALSE; - low_on = FALSE; - if ((max_targ > 8) && - ((external_present == 0) || (internal68_present == 0))) + if ( !(p->features & AHC_ULTRA2) && + (internal50_present && internal68_present && external_present) ) { - high_on = TRUE; + printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n", + p->host_no); + printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be " + "in use at a time!\n", p->host_no); + /* + * Force termination (low and high byte) on. This is safer than + * leaving it completely off, especially since this message comes + * most often from motherboard controllers that don't even have 3 + * connectors, but instead are failing the cable detection. + */ + internal50_present = external_present = 0; + enableSE_high = enableSE_low = 1; } - if ( ( (internal50_present ? 1 : 0) + - (internal68_present ? 1 : 0) + - (external_present ? 1 : 0) ) <= 1) + if ((max_target > 8) && + ((external_present == 0) || (internal68_present == 0) || + (enableSE_high != 0))) { - low_on = TRUE; + brddat |= BRDDAT6; + p->flags |= AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + p->host_no); } - - if (internal50_present && internal68_present && external_present) + + if ( (((internal50_present ? 1 : 0) + + (internal68_present ? 1 : 0) + + (external_present ? 1 : 0)) <= 1) || + (enableSE_low != 0) ) { - printk(KERN_INFO "aic7xxx: Illegal cable configuration!! Only two\n"); - printk(KERN_INFO "aic7xxx: connectors on the SCSI controller may be " - "in use at a time!\n"); + if (p->features & AHC_ULTRA2) + brddat |= BRDDAT5; + else + sxfrctl1 |= STPWEN; + p->flags |= AHC_TERM_ENB_SE_LOW; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", + p->host_no); } - if (high_on == TRUE) - write_brdctl(p, BRDDAT6); - else - write_brdctl(p, 0); - - if (low_on == TRUE) - *sxfrctl1 |= STPWEN; - - if (max_targ > 8) + if (enableLVD_low != 0) { - printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", - low_on ? "ON" : "OFF", high_on ? "ON" : "OFF"); + sxfrctl1 |= STPWEN; + p->flags |= AHC_TERM_ENB_LVD; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) LVD Low byte termination Enabled\n", + p->host_no); } - else + + if (enableLVD_high != 0) { - printk(KERN_INFO "aic7xxx: Termination %s\n", - low_on ? "Enabled" : "Disabled"); + brddat |= BRDDAT4; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) LVD High byte termination Enabled\n", + p->host_no); } } else { - if (adapter_control & CFSTERM) - *sxfrctl1 |= STPWEN; - - aic_outb(p, SEEMS | SEECS, SEECTL); - /* - * Configure high byte termination. - */ - if (adapter_control & CFWSTERM) + if (p->adapter_control & CFSTERM) { - write_brdctl(p, BRDDAT6); - } - else - { - write_brdctl(p, 0); + if (p->features & AHC_ULTRA2) + brddat |= BRDDAT5; + else + sxfrctl1 |= STPWEN; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", + p->host_no); } - if (aic7xxx_verbose & VERBOSE_PROBE2) + + if (p->adapter_control & CFWSTERM) { - printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n", - (adapter_control & CFSTERM) ? "ON" : "OFF", - (adapter_control & CFWSTERM) ? "ON" : "OFF"); + brddat |= BRDDAT6; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + p->host_no); } } + write_brdctl(p, brddat); release_seeprom(p); + aic_outb(p, sxfrctl1, SXFRCTL1); } } @@ -6200,7 +7393,12 @@ aic_outb(p, i, SCBPTR); aic_outb(p, 0, SCB_CONTROL); /* Clear the control byte. */ aic_outb(p, i + 1, SCB_NEXT); /* Set the next pointer. */ + aic_outb(p, i - 1, SCB_PREV); /* Set the prev pointer. */ aic_outb(p, SCB_LIST_NULL, SCB_TAG); /* Make the tag invalid. */ + aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS); /* no busy untagged */ + aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */ + aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2); + aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3); } /* Make sure the last SCB terminates the free list. */ @@ -6212,6 +7410,11 @@ aic_outb(p, 0, SCB_CONTROL); p->scb_data->maxhscbs = i; + /* + * Use direct indexing instead for speed + */ + if ( i == AIC7XXX_MAXSCB ) + p->flags &= ~AHC_PAGESCBS; } } @@ -6230,9 +7433,7 @@ int i, result; int max_targets; int found = 1; - unsigned char target_settings; - unsigned char term, scsi_conf, sxfrctl1; - unsigned short ultraenable = 0; + unsigned char term, scsi_conf; struct Scsi_Host *host; /* @@ -6253,11 +7454,11 @@ host->n_io_port = 0xFF; host->base = (unsigned char *) p->mbase; host->irq = p->irq; - if (p->type & AHC_WIDE) + if (p->features & AHC_WIDE) { host->max_id = 16; } - if (p->type & AHC_TWIN) + if (p->features & AHC_TWIN) { host->max_channel = 1; } @@ -6265,6 +7466,7 @@ p->host = host; p->last_reset = 0; p->host_no = host->host_no; + host->unique_id = p->instance; p->isr_count = 0; p->next = NULL; p->completeq.head = NULL; @@ -6287,7 +7489,7 @@ for (i = 0; i < MAX_TARGETS; i++) { p->dev_commands_sent[i] = 0; - p->dev_flags[i] = DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR; + p->dev_flags[i] = 0; p->dev_active_cmds[i] = 0; p->dev_last_reset[i] = 0; p->dev_last_queue_full[i] = 0; @@ -6300,19 +7502,16 @@ p->dev_timer[i].expires = 0; p->dev_timer[i].data = (unsigned long)p; p->dev_timer[i].function = (void *)aic7xxx_timer; - p->syncinfo[i].period = 0; - p->syncinfo[i].offset = 0; } printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no, board_names[p->board_name_index]); - switch(p->type & 0x1ff1) + switch(p->chip) { - case AHC_AIC7770: - case AHC_274: + case (AHC_AIC7770|AHC_EISA): printk("EISA slot %d\n", p->pci_device_fn); break; - case AHC_284: + case (AHC_AIC7770|AHC_VL): printk("VLB slot %d\n", p->pci_device_fn); break; default: @@ -6320,7 +7519,7 @@ PCI_FUNC(p->pci_device_fn)); break; } - if (p->type & AHC_TWIN) + if (p->features & AHC_TWIN) { printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ", p->host_no, p->scsi_id, p->scsi_id_b); @@ -6331,7 +7530,7 @@ channel = ""; - if ((p->type & AHC_39x) != 0) + if ((p->flags & AHC_MULTI_CHANNEL) != 0) { channel = " A"; @@ -6340,7 +7539,7 @@ channel = (p->flags & AHC_CHNLB) ? " B" : " C"; } } - if (p->type & AHC_WIDE) + if (p->features & AHC_WIDE) { printk(KERN_INFO "(scsi%d) Wide ", p->host_no); } @@ -6357,257 +7556,190 @@ */ detect_maxscb(p); printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs); - printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n", - p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis", - p->base, p->irq); - printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n", - p->host_no, p->mbase, (unsigned long)p->maddr); - - + if (aic7xxx_verbose & VERBOSE_PROBE2) + { + printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n", + p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis", + p->base, p->irq); + printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n", + p->host_no, p->mbase, (unsigned long)p->maddr); + } +#ifdef CONFIG_PCI /* - * Register IRQ with the kernel. Only allow sharing IRQs with - * PCI devices. + * Now that we know our instance number, we can set the flags we need to + * force termination if need be. */ - if ((p->type & AHC_AIC7770) == AHC_AIC7770) + if (aic7xxx_stpwlev != -1) { - result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p)); + /* + * This option only applies to PCI controllers. + */ + if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) + { + unsigned char devconfig; + +#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92) + pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig); +#else + pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, + DEVCONFIG, &devconfig); +#endif + if ( (aic7xxx_stpwlev >> p->instance) & 0x01 ) + { + devconfig |= 0x02; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("(scsi%d) Force setting STPWLEV bit\n", p->host_no); + } + else + { + devconfig &= ~0x02; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("(scsi%d) Force clearing STPWLEV bit\n", p->host_no); + } +#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92) + pci_write_config_byte(p->pdev, DEVCONFIG, devconfig); +#else + pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, + DEVCONFIG, devconfig); +#endif + } } - else +#endif + + /* + * That took care of devconfig and stpwlev, now for the actual termination + * settings. + */ + if (aic7xxx_override_term != -1) { - result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ, - "aic7xxx", p)); - if (result < 0) + /* + * Again, this only applies to PCI controllers. We don't have problems + * with the termination on 274x controllers to the best of my knowledge. + */ + if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) { - result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ, - "aic7xxx", p)); + unsigned char term_override; + + term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f); + p->adapter_control &= + ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM); + if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) ) + { + p->adapter_control |= CFLVDSTERM; + } + if (term_override & 0x02) + { + p->adapter_control |= CFWSTERM; + } + if (term_override & 0x01) + { + p->adapter_control |= CFSTERM; + } } } - if (result < 0) + + if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) ) { - printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring.\n", - p->host_no, p->irq); - return (0); + if (p->features & AHC_SPIOCAP) + { + if ( aic_inb(p, SPIOCAP) & SSPIOCPS ) + /* + * Update the settings in sxfrctl1 to match the termination + * settings. + */ + configure_termination(p); + } + else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870) + { + configure_termination(p); + } } /* + * Clear out any possible pending interrupts. + */ + aic7xxx_clear_intstat(p); + + /* * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels */ - if (p->type & AHC_TWIN) + if (p->features & AHC_TWIN) { - /* - * The controller is gated to channel B after a chip reset; set - * bus B values first. - */ + /* Select channel B */ + aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL); + term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0; aic_outb(p, p->scsi_id_b, SCSIID); scsi_conf = aic_inb(p, SCSICONF + 1); - sxfrctl1 = aic_inb(p, SXFRCTL1); - aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | + aic_outb(p, DFON | SPIOEN, SXFRCTL0); + aic_outb(p, (scsi_conf & ENSPCHK) | term | ENSTIMER | ACTNEGEN, SXFRCTL1); + aic_outb(p, 0, SIMODE0); aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); - if (p->type & AHC_ULTRA) - { - aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0); - } - else - { - aic_outb(p, DFON | SPIOEN, SXFRCTL0); - } - if ( (p->type & AHC_AIC7770) == AHC_AIC7770 ) - { - scsi_conf &= ~0x07; - scsi_conf |= p->scsi_id_b; - aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF + 1); - } - if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) ) - { - /* Reset SCSI bus B. */ - if (aic7xxx_verbose & VERBOSE_PROBE) - printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no); - - aic7xxx_reset_current_bus(p); - } + aic_outb(p, 0, SCSIRATE); /* Select channel A */ - aic_outb(p, SELNARROW, SBLKCTL); + aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL); } - term = ((p->flags & AHC_TERM_ENB_A) != 0) ? STPWEN : 0; - aic_outb(p, p->scsi_id, SCSIID); + term = ((p->flags & AHC_TERM_ENB_SE_LOW) != 0) ? STPWEN : 0; + if (p->features & AHC_ULTRA2) + aic_outb(p, p->scsi_id, SCSIID_ULTRA2); + else + aic_outb(p, p->scsi_id, SCSIID); scsi_conf = aic_inb(p, SCSICONF); - sxfrctl1 = aic_inb(p, SXFRCTL1); - aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | + aic_outb(p, DFON | SPIOEN, SXFRCTL0); + aic_outb(p, (scsi_conf & ENSPCHK) | term | ENSTIMER | ACTNEGEN, SXFRCTL1); + aic_outb(p, 0, SIMODE0); aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); - if (p->type & AHC_ULTRA) + aic_outb(p, 0, SCSIRATE); + if ( p->features & AHC_ULTRA2) + aic_outb(p, 0, SCSIOFFSET); + + /* + * Look at the information that board initialization or the board + * BIOS has left us. In the lower four bits of each target's + * scratch space any value other than 0 indicates that we should + * initiate synchronous transfers. If it's zero, the user or the + * BIOS has decided to disable synchronous negotiation to that + * target so we don't activate the needsdtr flag. + */ + if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0) { - aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0); + max_targets = 8; } else { - aic_outb(p, DFON | SPIOEN, SXFRCTL0); + max_targets = 16; } - if ( (p->type & AHC_AIC7770) == AHC_AIC7770 ) + + if (!(aic7xxx_no_reset)) { - scsi_conf &= ~0x07; - scsi_conf |= p->scsi_id; - aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF); - } - - - if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) ) - { - /* Reset SCSI bus A. */ - if (aic7xxx_verbose & VERBOSE_PROBE) - { /* In case we are a 3940, 3985, or 7895, print the right channel */ - char *channel = ""; - if (p->flags & AHC_MULTI_CHANNEL) - { - channel = " A"; - if (p->flags & (AHC_CHNLB|AHC_CHNLC)) - channel = (p->flags & AHC_CHNLB) ? " B" : " C"; - } - printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel); - } - - aic7xxx_reset_current_bus(p); - /* - * Delay for the reset delay. + * If we reset the bus, then clear the transfer settings, else leave + * them be */ - if (!reset_delay) - aic7xxx_delay(AIC7XXX_RESET_DELAY); - } - - /* - * Look at the information that board initialization or the board - * BIOS has left us. In the lower four bits of each target's - * scratch space any value other than 0 indicates that we should - * initiate synchronous transfers. If it's zero, the user or the - * BIOS has decided to disable synchronous negotiation to that - * target so we don't activate the needsdtr flag. - */ - p->needsdtr_copy = 0x0; - p->sdtr_pending = 0x0; - p->needwdtr_copy = 0x0; - p->wdtr_pending = 0x0; - p->tagenable = 0x0; - p->ultraenb = 0x0; - p->discenable = 0xffff; - if ((p->type & (AHC_TWIN|AHC_WIDE)) == 0) - { - max_targets = 8; - } - else - { - max_targets = 16; - } - - /* - * Grab the disconnection disable table and invert it for our needs - */ - if (p->flags & AHC_USEDEFAULTS) - { - printk(KERN_INFO "(scsi%d) Host adapter BIOS disabled. Using default SCSI " - "device parameters.\n", p->host_no); - if (p->type & AHC_ULTRA) - p->ultraenb = 0xffff; - p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B; - } - else - { - p->discenable = ~((aic_inb(p, DISC_DSB + 1) << 8) | - aic_inb(p, DISC_DSB)); - if (p->type & AHC_ULTRA) - p->ultraenb = (aic_inb(p, ULTRA_ENB + 1) << 8) | - aic_inb(p, ULTRA_ENB); - } - - for (i = 0; i < max_targets; i++) - { - if (p->flags & AHC_USEDEFAULTS) - { - target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */ - p->needsdtr_copy |= (0x01 << i); - p->needwdtr_copy |= (0x01 << i); - if (p->type & AHC_ULTRA) - ultraenable |= (0x01 << i); - } - else + for (i = 0; i < max_targets; i++) { - target_settings = aic_inb(p, TARG_SCRATCH + i); - if (target_settings & 0x0F) - { - p->needsdtr_copy |= (0x01 << i); - /* - * Default to asynchronous transfers (0 offset) - */ - target_settings &= 0xF0; - } - if (target_settings & 0x80) + aic_outb(p, 0, TARG_SCSIRATE + i); + if (p->features & AHC_ULTRA2) { - p->needwdtr_copy |= (0x01 << i); - /* - * Clear the wide flag. When wide negotiation is successful, - * we'll enable it. - */ - target_settings &= 0x7F; + aic_outb(p, 0, TARG_OFFSET + i); } + p->transinfo[i].cur_offset = 0; + p->transinfo[i].cur_period = 0; + p->transinfo[i].cur_width = MSG_EXT_WDTR_BUS_8_BIT; } /* - * If we reset the bus, then clear the transfer ssettings, else leave - * them be + * If we reset the bus, then clear the transfer settings, else leave + * them be. */ - if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) ) - aic_outb(p, target_settings, TARG_SCRATCH + i); - if (p->needsdtr_copy & (0x01 << i)) - { - short sxfr, j; - - sxfr = target_settings & SXFR; - if ((p->ultraenb & (1 << i)) != 0) - { - /* Want an ultra speed in the table */ - sxfr |= 0x100; - } - for (j = 0; j < NUMBER(aic7xxx_syncrates); j++) - { - if (sxfr == aic7xxx_syncrates[j].rate) - break; - } - p->syncinfo[i].period = aic7xxx_syncrates[j].period; - p->syncinfo[i].offset = - (p->type & AHC_WIDE) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT; - } - else - { - p->syncinfo[i].period = 0; - p->syncinfo[i].offset = 0; - } - } - - /* - * If we are not wide, forget WDTR. This makes the driver - * work on some cards that don't leave these fields cleared - * when BIOS is not installed. - */ - if ( !(p->type & AHC_WIDE)) - { - p->needwdtr_copy = 0; - } - p->needsdtr = p->needsdtr_copy; - p->needwdtr = p->needwdtr_copy; - - /* - * If we reset the bus, then clear the transfer ssettings, else leave - * them be - */ - if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) ) - { aic_outb(p, 0, ULTRA_ENB); aic_outb(p, 0, ULTRA_ENB + 1); + p->ultraenb = 0; } /* @@ -6617,21 +7749,35 @@ * routine should only allocate contiguous memory, but note that * this could be a problem if kmalloc() is changed. */ - if (p->scb_data->hscbs == NULL) { size_t array_size; unsigned int hscb_physaddr; + unsigned long temp; array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb); - p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC); + if (p->scb_data->hscbs == NULL) + { + /* + * A little padding so we can align thing the way we want + */ + p->scb_data->hscbs = kmalloc(array_size + 0x1f, GFP_ATOMIC); + } if (p->scb_data->hscbs == NULL) { printk("(scsi%d) Unable to allocate hardware SCB array; " "failing detection.\n", p->host_no); - release_region(p->base, MAXREG - MINREG); - free_irq(p->irq, p); + p->irq = 0; return(0); } + /* + * Save the actual kmalloc buffer pointer off, then align our + * buffer to a 32 byte boundary + */ + p->scb_data->hscb_kmalloc_ptr = p->scb_data->hscbs; + temp = (unsigned long)p->scb_data->hscbs; + temp += 0x1f; + temp &= ~0x1f; + p->scb_data->hscbs = (struct aic7xxx_hwscb *)temp; /* At least the control byte of each SCB needs to be 0. */ memset(p->scb_data->hscbs, 0, array_size); @@ -6648,12 +7794,19 @@ aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1); aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2); aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3); + } - /* The Q-FIFOs we just set up are all empty */ - aic_outb(p, 0, QINPOS); - aic_outb(p, 0, KERNEL_QINPOS); - aic_outb(p, 0, QOUTPOS); - + /* The Q-FIFOs we just set up are all empty */ + aic_outb(p, 0, QINPOS); + aic_outb(p, 0, KERNEL_QINPOS); + aic_outb(p, 0, QOUTPOS); + + if(p->features & AHC_QUEUE_REGS) + { + aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA); + aic_outb(p, 0, SDSCB_QOFF); + aic_outb(p, 0, SNSCB_QOFF); + aic_outb(p, 0, HNSCB_QOFF); } /* @@ -6666,32 +7819,137 @@ * Message out buffer starts empty */ aic_outb(p, MSG_NOOP, MSG_OUT); + aic_outb(p, MSG_NOOP, LAST_MSG); + + /* + * Set all the other asundry items that haven't been set yet. + * This includes just dumping init values to a lot of registers simply + * to make sure they've been touched and are ready for use parity wise + * speaking. + */ + aic_outb(p, 0, TMODE_CMDADDR); + aic_outb(p, 0, TMODE_CMDADDR + 1); + aic_outb(p, 0, TMODE_CMDADDR + 2); + aic_outb(p, 0, TMODE_CMDADDR + 3); + aic_outb(p, 0, TMODE_CMDADDR_NEXT); + + /* + * Link us into the list of valid hosts + */ + p->next = first_aic7xxx; + first_aic7xxx = p; + + /* + * Clear out any possible pending interrupts, again. + */ + aic7xxx_clear_intstat(p); + + /* + * Allocate the first set of scbs for this controller. This is to stream- + * line code elsewhere in the driver. If we have to check for the existence + * of scbs in certain code sections, it slows things down. However, as + * soon as we register the IRQ for this card, we could get an interrupt that + * includes possibly the SCSI_RSTI interrupt. If we catch that interrupt + * then we are likely to segfault if we don't have at least one chunk of + * SCBs allocated or add checks all through the reset code to make sure + * that the SCBs have been allocated which is an invalid running condition + * and therefore I think it's preferable to simply pre-allocate the first + * chunk of SCBs. + */ + aic7xxx_allocate_scb(p); /* * Load the sequencer program, then re-enable the board - * resetting the AIC-7770 disables it, leaving the lights - * on with nobody home. On the PCI bus you *may* be home, - * but then your mailing address is dynamically assigned - * so no one can find you anyway :-) + * on with nobody home. */ aic7xxx_loadseq(p); - if ( (p->type & AHC_AIC7770) == AHC_AIC7770 ) + if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 ) { aic_outb(p, ENABLE, BCTL); /* Enable the boards BUS drivers. */ } - /* - * Link us into the list of valid hosts - */ - p->next = first_aic7xxx; - first_aic7xxx = p; + if ( !(aic7xxx_no_reset) ) + { + if (p->features & AHC_TWIN) + { + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no); + aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL); + aic7xxx_reset_current_bus(p); + aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL); + } + /* Reset SCSI bus A. */ + if (aic7xxx_verbose & VERBOSE_PROBE2) + { /* In case we are a 3940, 3985, or 7895, print the right channel */ + char *channel = ""; + if (p->flags & AHC_MULTI_CHANNEL) + { + channel = " A"; + if (p->flags & (AHC_CHNLB|AHC_CHNLC)) + channel = (p->flags & AHC_CHNLB) ? " B" : " C"; + } + printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel); + } + + /* + * Some of the new Ultra2 chipsets need a longer delay after a chip + * reset than just the init setup creates, so we have to delay here + * before we go into a reset in order to make the chips happy. + */ + if (p->features & AHC_ULTRA2) + mdelay(250); + aic7xxx_reset_current_bus(p); + /* + * Delay for the reset delay. + */ + if (!reset_delay) + aic7xxx_delay(AIC7XXX_RESET_DELAY); + } + else + { + if (!reset_delay) + { + printk(KERN_INFO "(scsi%d) Not resetting SCSI bus. Note: Don't use " + "the no_reset\n", p->host_no); + printk(KERN_INFO "(scsi%d) option unless you have a verifiable need " + "for it.\n", p->host_no); + printk(KERN_INFO "(scsi%d) The no_reset option is known to break some " + "systems,\n", p->host_no); + printk(KERN_INFO "(scsi%d) and is not supported by the driver author\n", + p->host_no); + aic7xxx_delay(AIC7XXX_RESET_DELAY); + } + } + /* - * Unpause the sequencer before returning and enable - * interrupts - we shouldn't get any until the first - * command is sent to us by the high-level SCSI code. + * Register IRQ with the kernel. Only allow sharing IRQs with + * PCI devices. */ + if (!(p->chip & AHC_PCI)) + { + result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p)); + } + else + { + result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ, + "aic7xxx", p)); + if (result < 0) + { + result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ, + "aic7xxx", p)); + } + } + if (result < 0) + { + printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring " + "controller.\n", p->host_no, p->irq); + p->irq = 0; + return (0); + } + unpause_sequencer(p, /* unpause_always */ TRUE); return (found); @@ -6708,12 +7966,9 @@ int aic7xxx_chip_reset(struct aic7xxx_host *p) { - unsigned char hcntrl; + unsigned char sblkctl; int wait; - /* Retain the IRQ type across the chip reset. */ - hcntrl = (aic_inb(p, HCNTRL) & IRQMS) | INTEN; - /* * For some 274x boards, we must clear the CHIPRST bit and pause * the sequencer. For some reason, this makes the driver work. @@ -6724,29 +7979,26 @@ * In the future, we may call this function as a last resort for * error handling. Let's be nice and not do any unecessary delays. */ - wait = 1000; /* 1 second (1000 * 1000 usec) */ - while ((wait > 0) && ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0)) + wait = 1000; /* 1 second (1000 * 1 msec) */ + while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK)) { - udelay(1000); /* 1 msec = 1000 usec */ - wait = wait - 1; + mdelay(1); /* 1 msec */ } - if ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0) - { - printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n"); - } - - aic_outb(p, hcntrl | PAUSE, HCNTRL); + pause_sequencer(p); - switch( aic_inb(p, SBLKCTL) & 0x0a ) + sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE); + if (p->chip & AHC_PCI) + sblkctl &= ~SELBUSB; + switch( sblkctl ) { case 0: /* normal narrow card */ break; case 2: /* Wide card */ - p->type |= AHC_WIDE; + p->features |= AHC_WIDE; break; case 8: /* Twin card */ - p->type |= AHC_TWIN; + p->features |= AHC_TWIN; p->flags |= AHC_MULTI_CHANNEL; break; default: /* hmmm...we don't know what this is */ @@ -6770,6 +8022,7 @@ { struct aic7xxx_host *p = NULL; struct Scsi_Host *host; + int i; /* * Allocate a storage area by registering us with the mid-level @@ -6781,6 +8034,7 @@ { p = (struct aic7xxx_host *) host->hostdata; memset(p, 0, sizeof(struct aic7xxx_host)); + *p = *temp; p->host = host; p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC); @@ -6795,34 +8049,20 @@ * For some reason we don't have enough memory. Free the * allocated memory for the aic7xxx_host struct, and return NULL. */ + release_region(p->base, MAXREG - MINREG); scsi_unregister(host); - p = NULL; + return(NULL); } - if (p != NULL) - { - p->host_no = host->host_no; - p->base = temp->base; - p->mbase = temp->mbase; - p->maddr = temp->maddr; - p->flags = temp->flags; - p->type = temp->type; - p->unpause = temp->unpause; - p->pause = temp->pause; - p->pci_bus = temp->pci_bus; - p->pci_device_fn = temp->pci_device_fn; - p->bios_address = temp->bios_address; - p->irq = temp->irq; - p->scsi_id = temp->scsi_id; - p->scsi_id_b = temp->scsi_id_b; - p->discenable = temp->discenable; - p->ultraenb = temp->ultraenb; - p->tagenable = 0; - p->orderedtag = 0; - p->board_name_index = temp->board_name_index; - p->adapter_control = temp->adapter_control; - p->bios_control = temp->bios_control; - DRIVER_LOCK_INIT + p->host_no = host->host_no; + p->tagenable = 0; + p->orderedtag = 0; + for (i=0; itransinfo[i].goal_period = 0; + p->transinfo[i].goal_offset = 0; + p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT; } + DRIVER_LOCK_INIT } return (p); } @@ -6838,28 +8078,35 @@ static void aic7xxx_free(struct aic7xxx_host *p) { - int i, jump; + int i; /* * Free the allocated hardware SCB space. */ - if (p->scb_data->hscbs != NULL) + if (p->scb_data != NULL) { - kfree(p->scb_data->hscbs); - } - /* - * Free the driver SCBs. These were allocated on an as-need - * basis. However, we allocated them 30 at a time up until the - * very last allocation (if there was one). So, we need to free - * every 30th pointer to free the array (this also frees the - * SG_array structs as well). - * - * Note, on 64 bit machines we allocate 29 at a time instead. - */ - jump = (sizeof(int) == sizeof(void *)) ? 30 : 29; - for (i = 0; i < p->scb_data->numscbs; i += jump) - { - kfree(p->scb_data->scb_array[i]); + if (p->scb_data->hscbs != NULL) + { + kfree(p->scb_data->hscb_kmalloc_ptr); + p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL; + } + /* + * Free the driver SCBs. These were allocated on an as-need + * basis. We allocated these in groups depending on how many + * we could fit into a given amount of RAM. The tail SCB for + * these allocations has a pointer to the alloced area. + */ + for (i = 0; i < p->scb_data->numscbs; i++) + { + if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL) + kfree(p->scb_data->scb_array[i]->kmalloc_ptr); + p->scb_data->scb_array[i] = NULL; + } + + /* + * Free the SCB data area. + */ + kfree(p->scb_data); } /* @@ -6874,10 +8121,6 @@ kfree(p->dev_sdtr_cmnd[i]); } - /* - * Free the SCB data area. - */ - kfree(p->scb_data); } /*+F************************************************************************* @@ -6888,12 +8131,12 @@ * Load the seeprom and configure adapter and target settings. * Returns 1 if the load was successful and 0 otherwise. *-F*************************************************************************/ -static int -load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1) +static void +aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1) { int have_seeprom = 0; - int i, max_targets; - unsigned char target_settings, scsi_conf; + int i, max_targets, mask; + unsigned char scsirate, scsi_conf; unsigned short scarray[128]; struct seeprom_config *sc = (struct seeprom_config *) scarray; @@ -6901,42 +8144,31 @@ { printk(KERN_INFO "aic7xxx: Loading serial EEPROM..."); } - switch (p->type & 0x00001ff1) + switch (p->chip) { - case AHC_AIC7770: /* None of these adapters have seeproms. */ - case AHC_274: + case (AHC_AIC7770|AHC_EISA): /* None of these adapters have seeproms. */ + if (aic_inb(p, SCSICONF) & TERM_ENB) + p->flags |= AHC_TERM_ENB_A; + if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) ) + p->flags |= AHC_TERM_ENB_B; break; - case AHC_284: + case (AHC_AIC7770|AHC_VL): have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray); break; - case AHC_AIC7850: /* The 2910B is a 7850 with a seeprom. */ - case AHC_294AU: - case AHC_AIC7870: /* For these controllers we try the three possible */ - case AHC_AIC7895: /* SEEPROM read types. If none works, then we are */ - case AHC_294: /* SOL. This should catch any SEEPROM variety */ - case AHC_394: /* Adaptec or some motherboard manufacturer might */ - case AHC_294U: /* throw at us, and since we perform a checksum */ - case AHC_394U: /* during the read, we should get bogus seeprom */ - case AHC_AIC7860: /* reads. */ - case AHC_AIC7880: - case AHC_398: - case AHC_398U: + default: have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), - scarray, sizeof(*sc)/2, C46); - if (!have_seeprom) - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), - scarray, sizeof(scarray)/2, C46); - if (!have_seeprom) - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), - scarray, sizeof(*sc)/2, C56_66); + scarray, p->sc_size, p->sc_type); if (!have_seeprom) - have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), - scarray, sizeof(scarray)/2, C56_66); - break; - - default: + { + if(p->sc_type == C46) + have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + scarray, p->sc_size, C56_66); + else + have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + scarray, p->sc_size, C46); + } break; } @@ -6944,10 +8176,31 @@ { if (aic7xxx_verbose & VERBOSE_PROBE2) { - printk("\naic7xxx: No SEEPROM available; using defaults.\n"); + printk("\naic7xxx: No SEEPROM available.\n"); + } + p->flags |= AHC_NEWEEPROM_FMT; + if (aic_inb(p, SCSISEQ) == 0) + { + p->flags |= AHC_USEDEFAULTS; + p->flags &= ~AHC_BIOS_ENABLED; + p->scsi_id = p->scsi_id_b = 7; + *sxfrctl1 |= STPWEN; + if (aic7xxx_verbose & VERBOSE_PROBE2) + { + printk("aic7xxx: Using default values.\n"); + } + } + else if (aic7xxx_verbose & VERBOSE_PROBE2) + { + printk("aic7xxx: Using leftover BIOS values.\n"); + } + if ( *sxfrctl1 & STPWEN ) + { + p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH; + sc->adapter_control &= ~CFAUTOTERM; + sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM; } - p->flags |= AHC_USEDEFAULTS; - p->flags &= ~AHC_BIOS_ENABLED; + p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B; } else { @@ -6957,15 +8210,25 @@ } /* + * Note things in our flags + */ + p->flags |= AHC_SEEPROM_FOUND; + + /* * Update the settings in sxfrctl1 to match the termination settings. */ *sxfrctl1 = 0; /* + * Get our SCSI ID from the SEEPROM setting... + */ + p->scsi_id = (sc->brtime_id & CFSCSIID); + + /* * First process the settings that are different between the VLB * and PCI adapter seeproms. */ - if (p->type & AHC_284) + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770) { /* VLB adapter seeproms */ if (sc->bios_control & CF284XEXTEND) @@ -6974,157 +8237,256 @@ if (sc->adapter_control & CF284XSTERM) { *sxfrctl1 |= STPWEN; - p->flags |= AHC_TERM_ENB_A; + p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH; } - /* - * The 284x SEEPROM doesn't have a max targets field. We - * set it to 16 to make sure we take care of the 284x-wide - * adapters. For narrow adapters, going through the extra - * 8 target entries will not cause any harm since they will - * will not be used. - * - * XXX - We should probably break out the bus detection - * from the register function so we can use it here - * to tell us how many targets there really are. - */ - max_targets = 16; } else { /* PCI adapter seeproms */ if (sc->bios_control & CFEXTEND) p->flags |= AHC_EXTEND_TRANS_A; + if (sc->bios_control & CFBIOSEN) + p->flags |= AHC_BIOS_ENABLED; + else + p->flags &= ~AHC_BIOS_ENABLED; if (sc->adapter_control & CFSTERM) { *sxfrctl1 |= STPWEN; - p->flags |= AHC_TERM_ENB_A; + p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH; } - - /* Limit to 16 targets just in case. */ - max_targets = MIN(sc->max_targets & CFMAXTARG, 16); } + p->sc = *sc; + } - p->discenable = 0; + p->discenable = 0; + + /* + * Limit to 16 targets just in case. The 2842 for one is known to + * blow the max_targets setting, future cards might also. + */ + max_targets = MIN(sc->max_targets & CFMAXTARG, + ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8)); + if (have_seeprom) + { for (i = 0; i < max_targets; i++) { - if( (p->type & AHC_ULTRA) && - !(sc->adapter_control & CFULTRAEN) && - (sc->device_flags[i] & CFSYNCHISULTRA) ) + if( ((p->features & AHC_ULTRA) && + !(sc->adapter_control & CFULTRAEN) && + (sc->device_flags[i] & CFSYNCHISULTRA)) || + (sc->device_flags[i] & CFNEWULTRAFORMAT) ) { p->flags |= AHC_NEWEEPROM_FMT; break; } } + } - for (i = 0; i < max_targets; i++) + for (i = 0; i < max_targets; i++) + { + mask = (0x01 << i); + if (!have_seeprom) { - target_settings = (sc->device_flags[i] & CFXFER) << 4; - if (sc->device_flags[i] & CFSYNCH) + if(aic_inb(p, SCSISEQ) != 0) { - target_settings |= SOFS; + /* + * OK...the BIOS set things up and left behind the settings we need. + * Just make our sc->device_flags[i] entry match what the card has + * set for this device. + */ + p->discenable = + ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) ); + p->ultraenb = + (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) ); + sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0; + if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) + sc->device_flags[i] |= CFWIDEB; + if (p->features & AHC_ULTRA2) + { + if (aic_inb(p, TARG_OFFSET + i)) + { + sc->device_flags[i] |= CFSYNCH; + sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07); + if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 ) + sc->device_flags[i] |= CFSYNCHISULTRA; + } + } + else + { + if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER) + { + sc->device_flags[i] |= CFSYNCH; + if (p->features & AHC_ULTRA) + sc->device_flags[i] |= ((p->ultraenb & mask) ? + CFSYNCHISULTRA : 0); + } + } } - if (sc->device_flags[i] & CFWIDEB) + else { - target_settings |= WIDEXFER; + /* + * Assume the BIOS has NOT been run on this card and nothing between + * the card and the devices is configured yet. + */ + sc->device_flags[i] = CFDISC; + if (p->features & AHC_WIDE) + sc->device_flags[i] |= CFWIDEB; + if (p->features & AHC_ULTRA2) + sc->device_flags[i] |= 3; + else if (p->features & AHC_ULTRA) + sc->device_flags[i] |= CFSYNCHISULTRA; + sc->device_flags[i] |= CFSYNCH; + aic_outb(p, 0, TARG_SCSIRATE + i); + if (p->features & AHC_ULTRA2) + aic_outb(p, 0, TARG_OFFSET + i); } - if (sc->device_flags[i] & CFDISC) + } + if (sc->device_flags[i] & CFDISC) + { + p->discenable |= mask; + } + if (p->flags & AHC_NEWEEPROM_FMT) + { + if (sc->device_flags[i] & CFSYNCHISULTRA) { - p->discenable |= (0x01 << i); + p->ultraenb |= mask; } - if (p->flags & AHC_NEWEEPROM_FMT) + else if (sc->device_flags[i] & CFNEWULTRAFORMAT) { - if (sc->device_flags[i] & CFSYNCHISULTRA) + if ( (sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03 ) { - p->ultraenb |= (0x01 << i); + sc->device_flags[i] &= ~CFXFER; + sc->device_flags[i] |= CFSYNCHISULTRA; + p->ultraenb |= mask; } } - else if (sc->adapter_control & CFULTRAEN) + } + else if (sc->adapter_control & CFULTRAEN) + { + p->ultraenb |= mask; + } + if ( (sc->device_flags[i] & CFSYNCH) == 0) + { + sc->device_flags[i] &= ~CFXFER; + p->ultraenb &= ~mask; + p->transinfo[i].user_offset = 0; + p->transinfo[i].user_period = 0; + p->transinfo[i].cur_offset = 0; + p->transinfo[i].cur_period = 0; + p->needsdtr_copy &= ~mask; + } + else + { + if (p->features & AHC_ULTRA2) { - p->ultraenb |= (0x01 << i); + p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2; + p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i); + scsirate = (sc->device_flags[i] & CFXFER) | + ((p->ultraenb & mask) ? 0x18 : 0x10); + p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate, + AHC_SYNCRATE_ULTRA2); + p->transinfo[i].cur_period = aic7xxx_find_period(p, + aic_inb(p, TARG_SCSIRATE + i), + AHC_SYNCRATE_ULTRA2); } - if ( ((target_settings & 0x70) == 0x40) && - (p->ultraenb & (0x01 << i)) ) + else { - target_settings &= ~0x70; - p->ultraenb &= ~(0x01 << i); + scsirate = (sc->device_flags[i] & CFXFER) << 4; + if (sc->device_flags[i] & CFWIDEB) + p->transinfo[i].user_offset = MAX_OFFSET_16BIT; + else + p->transinfo[i].user_offset = MAX_OFFSET_8BIT; + if (p->features & AHC_ULTRA) + { + short ultraenb; + ultraenb = aic_inb(p, ULTRA_ENB) | + (aic_inb(p, ULTRA_ENB + 1) << 8); + p->transinfo[i].user_period = aic7xxx_find_period(p, + scsirate, + (p->ultraenb & mask) ? + AHC_SYNCRATE_ULTRA : + AHC_SYNCRATE_FAST); + p->transinfo[i].cur_period = aic7xxx_find_period(p, + aic_inb(p, TARG_SCSIRATE + i), + (ultraenb & mask) ? + AHC_SYNCRATE_ULTRA : + AHC_SYNCRATE_FAST); + } + else + p->transinfo[i].user_period = aic7xxx_find_period(p, + scsirate, AHC_SYNCRATE_FAST); } - /* - * Don't output these settings if we aren't resetting the bus, instead, - * leave the devices current settings in place - */ - if (!(aic7xxx_no_reset)) - aic_outb(p, target_settings, TARG_SCRATCH + i); + p->needsdtr_copy |= mask; } - aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB); - aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1); - aic_outb(p, (p->ultraenb & 0xFF), ULTRA_ENB); - aic_outb(p, ((p->ultraenb >> 8) & 0xFF), ULTRA_ENB + 1); - - p->scsi_id = sc->brtime_id & CFSCSIID; - p->adapter_control = sc->adapter_control; - p->bios_control = sc->bios_control; - - if (p->bios_control & CFBIOSEN) + if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) ) { - p->flags &= ~AHC_USEDEFAULTS; - p->flags |= AHC_BIOS_ENABLED; + p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_16_BIT; + p->needwdtr_copy |= mask; } else { - p->flags &= ~AHC_BIOS_ENABLED; - p->flags |= AHC_USEDEFAULTS; + p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_8_BIT; + p->needwdtr_copy &= ~mask; } + p->transinfo[i].cur_width = + (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) ? + MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT; + } + aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB); + aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1); + p->needwdtr = p->needwdtr_copy; + p->needsdtr = p->needsdtr_copy; + p->wdtr_pending = p->sdtr_pending = 0; + + /* + * We set the p->ultraenb from the SEEPROM to begin with, but now we make + * it match what is already down in the card. If we are doing a reset + * on the card then this will get put back to a default state anyway. + * This allows us to not have to pre-emptively negotiate when using the + * no_reset option. + */ + if (p->features & AHC_ULTRA) + p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8); + + + scsi_conf = (p->scsi_id & HSCSIID); + + if(have_seeprom) + { + p->adapter_control = sc->adapter_control; + p->bios_control = sc->bios_control; - if ((p->type & 0x1ff1) == AHC_AIC7895) + switch (p->chip & AHC_CHIPID_MASK) { - if (p->adapter_control & CFBPRIMARY) - p->flags |= AHC_CHANNEL_B_PRIMARY; + case AHC_AIC7895: + case AHC_AIC7896: + if (p->adapter_control & CFBPRIMARY) + p->flags |= AHC_CHANNEL_B_PRIMARY; + default: + break; } - scsi_conf = (p->scsi_id & 0x7); if (sc->adapter_control & CFSPARITY) scsi_conf |= ENSPCHK; - /* - * The 7850 controllers with a seeprom, do not honor the CFRESETB - * flag in the seeprom. Assume that we want to reset the SCSI bus. - */ - if (sc->adapter_control & CFRESETB) - scsi_conf |= RESET_SCSI; - /* - * We may be a 2842, if so, preserve the TERM_ENB bit in scsi conf - */ - if ( (p->flags & AHC_TERM_ENB_A) && - ((p->type & AHC_AIC7770) == AHC_AIC7770) ) - scsi_conf |= TERM_ENB; - /* - * If this is an Ultra card, is Ultra mode enabled? If not, disable - * it in the host struct as well - */ - if ( (p->type & AHC_ULTRA) && - !(sc->adapter_control & CFULTRAEN) && - !(p->flags & AHC_NEWEEPROM_FMT) ) - p->type &= ~AHC_ULTRA; + } + else + { + scsi_conf |= ENSPCHK | RESET_SCSI; + } + /* + * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card. + * The 2842 and 2742 cards already have these registers set and we don't + * want to muck with them since we don't set all the bits they do. + */ + if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI ) + { /* Set the host ID */ aic_outb(p, scsi_conf, SCSICONF); /* In case we are a wide card */ aic_outb(p, p->scsi_id, SCSICONF + 1); - - if ((p->type & AHC_AIC7860) == AHC_AIC7860) - { - if ( aic_inb(p, SPIOCAP) & SSPIOCPS ) - /* - * Update the settings in sxfrctl1 to match the termination - * settings. - */ - configure_termination(p, sxfrctl1, sc->adapter_control, max_targets); - } - else if (have_seeprom && ((p->type & AHC_AIC7770) != AHC_AIC7770)) - configure_termination(p, sxfrctl1, sc->adapter_control, max_targets); } - return (have_seeprom); } /*+F************************************************************************* @@ -7146,8 +8508,10 @@ struct aic7xxx_host *current_p = NULL; struct aic7xxx_host *list_p = NULL; int found = 0; +#if defined(__i386__) || defined(__alpha__) ahc_flag_type flags = 0; - ahc_type type; + int type; +#endif unsigned char sxfrctl1; #if defined(__i386__) || defined(__alpha__) unsigned char hcntrl, hostconf; @@ -7181,7 +8545,7 @@ * EISA/VL-bus card signature probe. */ slot = MINSLOT; - while (slot <= MAXSLOT) + while ( (slot <= MAXSLOT) && !(aic7xxx_no_probe) ) { base = SLOTBASE(slot) + MINREG; @@ -7195,27 +8559,11 @@ continue; /* back to the beginning of the for loop */ } flags = 0; - type = aic7xxx_probe(slot, base + HID0, &flags); - switch (type) + type = aic7xxx_probe(slot, base + AHC_HID0, &flags); + if (type == -1) { - case AHC_AIC7770: - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at EISA %d\n", - board_names[2], slot); - break; - case AHC_274: - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at EISA %d\n", - board_names[3], slot); - break; - case AHC_284: - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at VLB %d\n", - board_names[4], slot); - break; - default: - slot++; - continue; /* back to the beginning of the while loop */ + slot++; + continue; } temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC); if (temp_p == NULL) @@ -7238,8 +8586,6 @@ temp_p->unpause = hcntrl | INTEN; temp_p->pause = hcntrl | PAUSE | INTEN; temp_p->base = base; - temp_p->type = type; - temp_p->flags = flags | AHC_PAGESCBS; temp_p->mbase = 0; temp_p->maddr = 0; temp_p->pci_bus = 0; @@ -7250,6 +8596,8 @@ temp_p->irq = 0; else temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F; + temp_p->flags |= AHC_PAGESCBS; + switch (temp_p->irq) { case 9: @@ -7272,6 +8620,7 @@ * We are commited now, everything has been checked and this card * has been found, now we just set it up */ + /* * Insert our new struct into the list at the end */ @@ -7293,12 +8642,18 @@ temp_p->flags |= AHC_EXTEND_TRANS_B; } - switch (temp_p->type & 0x1ff1) + switch (type) { - case AHC_AIC7770: + case 0: temp_p->board_name_index = 2; - case AHC_274: + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at EISA %d\n", + board_names[2], slot); + /* FALLTHROUGH */ + case 1: { + temp_p->chip = AHC_AIC7770 | AHC_EISA; + temp_p->features |= AHC_AIC7770_FE; temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL); /* @@ -7307,7 +8662,12 @@ * the mid-level SCSI code which channel is primary. */ if (temp_p->board_name_index == 0) + { temp_p->board_name_index = 3; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at EISA %d\n", + board_names[3], slot); + } if (temp_p->bios_control & CHANNEL_B_PRIMARY) { temp_p->flags |= AHC_CHANNEL_B_PRIMARY; @@ -7315,7 +8675,6 @@ if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED) { - temp_p->flags |= AHC_USEDEFAULTS; temp_p->flags &= ~AHC_BIOS_ENABLED; } else @@ -7324,79 +8683,46 @@ temp_p->flags |= AHC_BIOS_ENABLED; if ( (temp_p->bios_control & 0x20) == 0 ) { - switch(temp_p->bios_control & 0x07) - { - case 0x0: - temp_p->bios_address = 0xcc000; - break; - case 0x1: - temp_p->bios_address = 0xd0000; - break; - case 0x2: - temp_p->bios_address = 0xd4000; - break; - case 0x3: - temp_p->bios_address = 0xd8000; - break; - case 0x4: - temp_p->bios_address = 0xdc000; - break; - case 0x5: - temp_p->bios_address = 0xe0000; - break; - case 0x6: - temp_p->bios_address = 0xe4000; - break; - case 0x7: - temp_p->bios_address = 0xe8000; - break; - default: - break; /* can't get here */ - } + temp_p->bios_address = 0xcc000; + temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07)); } else { - switch(temp_p->bios_control & 0x06) - { - case 0x0: - temp_p->bios_address = 0xd0000; - break; - case 0x2: - temp_p->bios_address = 0xd8000; - break; - case 0x4: - temp_p->bios_address = 0xe0000; - break; - case 0x6: - temp_p->bios_address = 0xe8000; - break; - default: - break; /* can't get here */ - } + temp_p->bios_address = 0xd0000; + temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06)); } } temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8; temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1); - if (temp_p->flags & AHC_USEDEFAULTS) + if (temp_p->features & AHC_WIDE) { - temp_p->scsi_id = temp_p->scsi_id_b = 7; - temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B; + temp_p->scsi_id = temp_p->adapter_control & HWSCSIID; + temp_p->scsi_id_b = temp_p->scsi_id; } else { - if ( ((temp_p->adapter_control >> 8) & TERM_ENB) != 0 ) - temp_p->flags |= AHC_TERM_ENB_A; - if ( (temp_p->adapter_control & TERM_ENB) != 0 ) - temp_p->flags |= AHC_TERM_ENB_B; temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID; temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID; } + aic7xxx_load_seeprom(temp_p, &sxfrctl1); break; } - case AHC_284: - load_seeprom(temp_p, &sxfrctl1); + case 2: + case 3: + temp_p->chip = AHC_AIC7770 | AHC_VL; + temp_p->features |= AHC_AIC7770_FE; + if (type == 2) + temp_p->flags |= AHC_BIOS_ENABLED; + else + temp_p->flags &= ~AHC_BIOS_ENABLED; + if (aic_inb(temp_p, SCSICONF) & TERM_ENB) + sxfrctl1 = STPWEN; + aic7xxx_load_seeprom(temp_p, &sxfrctl1); temp_p->board_name_index = 4; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at VLB %d\n", + board_names[2], slot); switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL ) { case 0x00: @@ -7455,42 +8781,84 @@ { unsigned short vendor_id; unsigned short device_id; - ahc_type type; + ahc_chip chip; ahc_flag_type flags; + ahc_feature features; int board_name_index; - } const aic7xxx_pci_devices[] = { + unsigned short seeprom_size; + unsigned short seeprom_type; + } const aic_pdevs[] = { {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE, - AHC_FNONE, 1 }, + AHC_FNONE, AHC_FENONE, 1, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850, - AHC_PAGESCBS | AHC_USEDEFAULTS, 5 }, + AHC_PAGESCBS, AHC_AIC7850_FE, 5, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850, - AHC_PAGESCBS | AHC_USEDEFAULTS, 6 }, + AHC_PAGESCBS, AHC_AIC7850_FE, 6, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 7 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_294AU, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 8 }, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7860_FE, 7, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7860_FE, 8, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 9 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_294, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 10 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_394, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 11 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_398, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 12 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_294, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 13 }, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 9, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 10, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7870_FE, 11, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7870_FE, 12, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 13, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 14 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_294U, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 15 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_394U, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 16 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_398U, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 17 }, - {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_294U, - AHC_PAGESCBS | AHC_BIOS_ENABLED, 18 }, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 14, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 15, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7880_FE, 16, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7880_FE, 17, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, + 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 19 } + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7895_FE, 19, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7890_FE, 20, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7890_FE, 21, + 32, C46 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7896_FE, 22, + 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, + AHC_AIC7896_FE, 23, + 32, C56_66 }, }; unsigned short command; @@ -7498,7 +8866,6 @@ #ifdef MMAPIO unsigned long page_offset, base; #endif - struct aic7xxx_host *first_7895 = NULL; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) struct pci_dev *pdev = NULL; #else @@ -7507,17 +8874,17 @@ unsigned char pci_bus, pci_devfn, pci_irq; #endif - for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++) + for (i = 0; i < NUMBER(aic_pdevs); i++) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pdev = NULL; - while ((pdev = pci_find_device(aic7xxx_pci_devices[i].vendor_id, - aic7xxx_pci_devices[i].device_id, + while ((pdev = pci_find_device(aic_pdevs[i].vendor_id, + aic_pdevs[i].device_id, pdev))) #else index = 0; - while (!(pcibios_find_device(aic7xxx_pci_devices[i].vendor_id, - aic7xxx_pci_devices[i].device_id, + while (!(pcibios_find_device(aic_pdevs[i].vendor_id, + aic_pdevs[i].device_id, index++, &pci_bus, &pci_devfn)) ) #endif { @@ -7534,9 +8901,12 @@ GFP_ATOMIC)) != NULL ) { memset(temp_p, 0, sizeof(struct aic7xxx_host)); - temp_p->type = aic7xxx_pci_devices[i].type; - temp_p->flags = aic7xxx_pci_devices[i].flags; - temp_p->board_name_index = aic7xxx_pci_devices[i].board_name_index; + temp_p->chip = aic_pdevs[i].chip | AHC_PCI; + temp_p->flags = aic_pdevs[i].flags; + temp_p->features = aic_pdevs[i].features; + temp_p->board_name_index = aic_pdevs[i].board_name_index; + temp_p->sc_size = aic_pdevs[i].seeprom_size; + temp_p->sc_type = aic_pdevs[i].seeprom_type; /* * Read sundry information from PCI BIOS. @@ -7548,19 +8918,29 @@ temp_p->pci_device_fn = pdev->devfn; temp_p->base = pdev->base_address[0]; temp_p->mbase = pdev->base_address[1]; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pdev->devfn), + PCI_FUNC(temp_p->pdev->devfn)); pci_read_config_word(pdev, PCI_COMMAND, &command); if (aic7xxx_verbose & VERBOSE_PROBE2) { printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n", (int)command); } +#ifdef AIC7XXX_STRICT_PCI_SETUP command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +#else + command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +#endif if (aic7xxx_pci_parity == 0) command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); pci_write_config_word(pdev, PCI_COMMAND, command); - pci_read_config_dword(pdev, PCI_COMMAND, &devconfig); +#ifdef AIC7XXX_STRICT_PCI_SETUP + pci_read_config_dword(pdev, DEVCONFIG, &devconfig); if (aic7xxx_verbose & VERBOSE_PROBE2) { printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig); @@ -7574,15 +8954,16 @@ { devconfig |= 0x00000008; } - pci_write_config_dword(pdev, PCI_COMMAND, devconfig); - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at PCI %d/%d\n", - board_names[aic7xxx_pci_devices[i].board_name_index], - PCI_SLOT(temp_p->pdev->devfn), - PCI_FUNC(temp_p->pdev->devfn)); -#else + pci_write_config_dword(pdev, DEVCONFIG, devconfig); +#endif /* AIC7XXX_STRICT_PCI_SETUP */ +#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ temp_p->pci_bus = pci_bus; temp_p->pci_device_fn = pci_devfn; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk("aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE, &pci_irq); temp_p->irq = pci_irq; @@ -7598,12 +8979,17 @@ printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n", (int)command); } +#ifdef AIC7XXX_STRICT_PCI_SETUP command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +#else + command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; +#endif if (aic7xxx_pci_parity == 0) command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command); +#ifdef AIC7XXX_STRICT_PCI_SETUP pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, &devconfig); if (aic7xxx_verbose & VERBOSE_PROBE2) { @@ -7619,12 +9005,8 @@ devconfig |= 0x00000008; } pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig); - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at PCI %d/%d\n", - board_names[aic7xxx_pci_devices[i].board_name_index], - PCI_SLOT(temp_p->pci_device_fn), - PCI_FUNC(temp_p->pci_device_fn)); -#endif +#endif /* AIC7XXX_STRICT_PCI_SETUP */ +#endif /* LINUIX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ /* * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so @@ -7632,49 +9014,24 @@ */ temp_p->base &= PCI_BASE_ADDRESS_IO_MASK; temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK; - temp_p->unpause = (aic_inb(temp_p, HCNTRL) & IRQMS) | INTEN; + temp_p->unpause = INTEN; temp_p->pause = temp_p->unpause | PAUSE; #ifdef MMAPIO - if((temp_p->type & AHC_AIC7850) != AHC_AIC7850) - { - base = temp_p->mbase & PAGE_MASK; - page_offset = temp_p->mbase - base; - /* - * replace the next line with this one if you are using 2.1.x: - * temp_p->maddr = ioremap(base, page_offset + 256); - */ + base = temp_p->mbase & PAGE_MASK; + page_offset = temp_p->mbase - base; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - temp_p->maddr = ioremap(base, page_offset + 256); + temp_p->maddr = ioremap_nocache(base, page_offset + 256); #else - temp_p->maddr = vremap(base, page_offset + 256); + temp_p->maddr = vremap(base, page_offset + 256); #endif - if(temp_p->maddr) - { - temp_p->maddr += page_offset; - } - } - else + if(temp_p->maddr) { -#ifdef __i386__ - /* - * Resort to PIO mode on these controllers and Intel hardware. - * For other hardware we need to either disable these controllers - * or do without MMAPed IO. However, for PPC, we can't do - * MMAPed IO (according to what I've heard) so we may be forced - * to just fail detection on those cards. - */ - temp_p->maddr = NULL; -#else - kfree(temp_p); - temp_p = NULL; - continue; -#endif /* __i386__ */ + temp_p->maddr += page_offset; } #endif - aic_outb(temp_p, temp_p->pause, HCNTRL); - while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ; + pause_sequencer(temp_p); /* * Clear out any pending PCI error status messages. Also set @@ -7691,7 +9048,10 @@ /* * Remember how the card was setup in case there is no seeprom. */ - temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID; + if (temp_p->features & AHC_ULTRA2) + temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID; + else + temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID; /* * Get current termination setting */ @@ -7704,11 +9064,17 @@ continue; } - switch (temp_p->type & 0x1ff1) + /* + * Doing a switch based upon i is really gross, but since Justin + * changed around the chip ID stuff, we can't use that any more. + * Since we don't scan the devices the same way as FreeBSD, we end + * up doing this gross hack in order to avoid totally splitting + * away from Justin's init code in ahc_pci.c + */ + switch (i) { - case AHC_394: /* 3940 */ - case AHC_394U: /* 3940-Ultra */ - temp_p->flags |= AHC_MULTI_CHANNEL; + case 7: /* 3940 */ + case 12: /* 3940-Ultra */ switch(PCI_SLOT(temp_p->pci_device_fn)) { case 5: @@ -7719,9 +9085,8 @@ } break; - case AHC_398: /* 3985 */ - case AHC_398U: /* 3985-Ultra */ - temp_p->flags |= AHC_MULTI_CHANNEL; + case 8: /* 3985 */ + case 13: /* 3985-Ultra */ switch(PCI_SLOT(temp_p->pci_device_fn)) { case 8: @@ -7735,54 +9100,44 @@ } break; - case AHC_AIC7895: - temp_p->flags |= AHC_MULTI_CHANNEL; + case 15: + case 18: + case 19: #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) if (PCI_FUNC(temp_p->pdev->devfn) != 0) { temp_p->flags |= AHC_CHNLB; } - pci_read_config_dword(pdev, DEVCONFIG, &devconfig); - devconfig = le32_to_cpu(devconfig); - devconfig |= SCBSIZE32; - devconfig = cpu_to_le32(devconfig); - pci_write_config_dword(pdev, DEVCONFIG, devconfig); + /* + * The 7895 is the only chipset that sets the SCBSIZE32 param + * in the DEVCONFIG register. The Ultra2 chipsets use + * the DSCOMMAND0 register instead. + */ + if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) + { + pci_read_config_dword(pdev, DEVCONFIG, &devconfig); + devconfig |= SCBSIZE32; + pci_write_config_dword(pdev, DEVCONFIG, devconfig); + } #else if (PCI_FUNC(temp_p->pci_device_fn) != 0) { temp_p->flags |= AHC_CHNLB; } - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, - &devconfig); - devconfig |= SCBSIZE32; - pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, - devconfig); -#endif - if (aic7xxx_7895_irq_hack != -1) + /* + * The 7895 is the only chipset that sets the SCBSIZE32 param + * in the DEVCONFIG register. The Ultra2 chipsets use + * the DSCOMMAND0 register instead. + */ + if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { - if (first_7895 == NULL) - { - printk(KERN_INFO "aic7xxx: Using 7895_irq_hack. Please " - "upgrade your motherboard BIOS\n"); - first_7895 = temp_p; - } - else if (aic7xxx_7895_irq_hack == 0) - { - if (temp_p->flags & AHC_CHNLB) - temp_p->irq = first_7895->irq; - else - first_7895->irq = temp_p->irq; - first_7895 = NULL; - } - else - { - if ( !(temp_p->flags & AHC_CHNLB) ) - temp_p->irq = first_7895->irq; - else - first_7895->irq = temp_p->irq; - first_7895 = NULL; - } + pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, + &devconfig); + devconfig |= SCBSIZE32; + pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, + devconfig); } +#endif break; default: break; @@ -7794,21 +9149,95 @@ * on 394x and 398x cards we'll end up reading the wrong settings * for channels B and C */ - if ( !(load_seeprom(temp_p, &sxfrctl1)) ) + switch (temp_p->chip & AHC_CHIPID_MASK) { - temp_p->flags |= AHC_USEDEFAULTS; - if (sxfrctl1 & STPWEN) - temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B; + case AHC_AIC7890: + case AHC_AIC7896: + aic_outb(temp_p, 0, SCAMCTL); + /* + * We used to set DPARCKEN in this register, but after talking + * to a tech from Adaptec, I found out they don't use that + * particular bit in their own register settings, and when you + * combine that with the fact that I determined that we were + * seeing Data-Path Parity Errors on things we shouldn't see + * them on, I think there is a bug in the silicon and the way + * to work around it is to disable this particular check. Also + * This bug only showed up on certain commands, so it seems to + * be pattern related or some such. The commands we would + * typically send as a linux TEST_UNIT_READY or INQUIRY command + * could cause it to be triggered, while regular commands that + * actually made reasonable use of the SG array capabilities + * seemed not to cause the problem. + */ + /* + aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) | + CACHETHEN | DPARCKEN | MPARCKEN | + USCBSIZE32 | CIOPARCKEN, + DSCOMMAND0); + */ + aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | + CACHETHEN | MPARCKEN | USCBSIZE32 | + CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0); + /* FALLTHROUGH */ + default: + /* + * We attempt to read a SEEPROM on *everything*. If we fail, + * then we fail, but this covers things like 2910c cards that + * now have SEEPROMs with their 7856 chipset that we would + * otherwise ignore. They still don't have a BIOS, but they + * have a SEEPROM that the SCSISelect utility on the Adaptec + * diskettes can configure. + */ + aic7xxx_load_seeprom(temp_p, &sxfrctl1); + break; + case AHC_AIC7850: + case AHC_AIC7860: + /* + * Set the DSCOMMAND0 register on these cards different from + * on the 789x cards. Also, read the SEEPROM as well. + */ + aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | + CACHETHEN | MPARCKEN) & ~DPARCKEN, + DSCOMMAND0); + aic7xxx_load_seeprom(temp_p, &sxfrctl1); + break; + case AHC_AIC7880: + /* + * Only set the DSCOMMAND0 register if this is a Rev B. + * chipset. For those, we also enable Ultra mode by + * force due to brain-damage on the part of some BIOSes + * We overload the devconfig variable here since we can. + */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_read_config_dword(pdev, DEVCONFIG, &devconfig); +#else + pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, + &devconfig); +#endif + if ((devconfig & 0xff) >= 1) + { + aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | + CACHETHEN | MPARCKEN) & ~DPARCKEN, + DSCOMMAND0); + } + aic7xxx_load_seeprom(temp_p, &sxfrctl1); + break; } + /* * and then we need another switch based on the type in order to * make sure the channel B primary flag is set properly on 7895 - * controllers....Arrrgggghhh!!! + * controllers....Arrrgggghhh!!! We also have to catch the fact + * that when you disable the BIOS on the 7895 on the Intel DK440LX + * motherboard, and possibly others, it only sets the BIOS disabled + * bit on the A channel...I think I'm starting to lean towards + * going postal.... */ - switch(temp_p->type & 0x1ff1) + switch(temp_p->chip & AHC_CHIPID_MASK) { case AHC_AIC7895: + case AHC_AIC7896: current_p = list_p; while(current_p != NULL) { @@ -7817,11 +9246,21 @@ PCI_SLOT(temp_p->pci_device_fn)) ) { if ( PCI_FUNC(current_p->pci_device_fn) == 0 ) + { temp_p->flags |= (current_p->flags & AHC_CHANNEL_B_PRIMARY); + temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS); + temp_p->flags |= + (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS)); + } else + { current_p->flags |= (temp_p->flags & AHC_CHANNEL_B_PRIMARY); + current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS); + current_p->flags |= + (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS)); + } } current_p = current_p->next; } @@ -7831,6 +9270,56 @@ } /* + * We do another switch based on i so that we can exclude all + * 3895 devices from the next option since the 3895 cards use + * shared external SCB RAM while all other cards have dedicated + * external SCB RAM per channel. Also exclude the 7850 and + * 7860 based stuff since they can have garbage in the bit + * that indicates external RAM and get some of this stuff + * wrong as a result. + */ + switch(temp_p->chip & AHC_CHIPID_MASK) + { + default: + break; + case AHC_AIC7895: + case AHC_AIC7896: +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_read_config_dword(pdev, DEVCONFIG, &devconfig); +#else + pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, + &devconfig); +#endif + if (temp_p->features & AHC_ULTRA2) + { + if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) + { + aic_outb(temp_p, + aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2, + DSCOMMAND0); + temp_p->flags |= AHC_EXTERNAL_SRAM; + devconfig |= EXTSCBPEN; + } + } + else if (devconfig & RAMPSM) + { + devconfig &= ~SCBRAMSEL; + devconfig |= EXTSCBPEN; + temp_p->flags |= AHC_EXTERNAL_SRAM; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) + pci_write_config_dword(pdev, DEVCONFIG, devconfig); +#else + pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, + devconfig); +#endif + if ( (temp_p->flags & AHC_EXTERNAL_SRAM) && + (temp_p->flags & AHC_CHNLB) ) + aic_outb(temp_p, 1, CCSCBBADDR); + break; + } + + /* * Take the LED out of diagnostic mode */ aic_outb(temp_p, @@ -7839,75 +9328,21 @@ /* * We don't know where this is set in the SEEPROM or by the - * BIOS, so we default to 100%. + * BIOS, so we default to 100%. On Ultra2 controllers, use 75% + * instead. */ - aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS); - - if (temp_p->flags & AHC_USEDEFAULTS) + if (temp_p->features & AHC_ULTRA2) { - int j; - unsigned char k; - /* - * Default setup; should only be used if the adapter does - * not have a SEEPROM. - */ - /* - * Check the target scratch area to see if someone set us - * up already. We are previously set up if the scratch - * area contains something other than all zeroes and ones. - */ - for (j = TARG_SCRATCH; j < 0x60; j++) - { - k = aic_inb(temp_p, j); - /* Check for all zeros and ones. Break out if we pass */ - if( (k != 0x00) && (k != 0xff) ) - break; - } - /* If j makes it to 0x60, then all entries are either 0x00 or - * 0xff. We would then assume we have *not* been initialized - * and drop through here. OTOH, if even one entry is inited, - * then as long as we appear to have a valid SCSI ID, we'll use - * the leftover BIOS values. - */ - if ((j != 0x60) && (temp_p->scsi_id != 0)) - { - temp_p->flags &= ~AHC_USEDEFAULTS; - if (aic7xxx_verbose & VERBOSE_PROBE2) - { - printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n"); - } - } - else - { - /* - * Assume only one connector and always turn on - * termination. - */ - temp_p->flags &= ~AHC_BIOS_ENABLED; - temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B; - sxfrctl1 = STPWEN; - temp_p->scsi_id = 7; - } - aic_outb(temp_p, (temp_p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI, - SCSICONF); - /* In case we are a wide card. */ - aic_outb(temp_p, temp_p->scsi_id, SCSICONF + 1); + aic_outb(temp_p, RD_DFTHRSH_75 | WR_DFTHRSH_75, DFF_THRSH); } - else /* not using defaults */ + else { - if (sxfrctl1 & STPWEN) - temp_p->flags |= AHC_TERM_ENB_A; + aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS); } if (aic7xxx_extended) temp_p->flags |= AHC_EXTEND_TRANS_A; - /* - * Put our termination setting into sxfrctl1 now that the - * generic initialization is complete. - */ - sxfrctl1 |= aic_inb(temp_p, SXFRCTL1); - aic_outb(temp_p, sxfrctl1, SXFRCTL1); if ( list_p == NULL ) { list_p = current_p = temp_p; @@ -7925,7 +9360,7 @@ else /* Well, we found one, but we couldn't get any memory */ { printk("aic7xxx: Found <%s>\n", - board_names[aic7xxx_pci_devices[i].board_name_index]); + board_names[aic_pdevs[i].board_name_index]); printk(KERN_INFO "aic7xxx: Unable to allocate device memory, " "skipping.\n"); } @@ -7946,87 +9381,74 @@ */ { - struct aic7xxx_host *vlb_enab, *vlb_disab, *pci; + struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL }; + struct aic7xxx_host *vlb, *pci; struct aic7xxx_host *prev_p; struct aic7xxx_host *p; unsigned char left; - prev_p = vlb_enab = vlb_disab = pci = NULL; + prev_p = vlb = pci = NULL; temp_p = list_p; while (temp_p != NULL) { - switch(temp_p->type) + switch(temp_p->chip & ~AHC_CHIPID_MASK) { - case AHC_AIC7770: - case AHC_274: - case AHC_284: - if (temp_p->flags & AHC_BIOS_ENABLED) + case AHC_EISA: + case AHC_VL: + { + p = temp_p; + if (p->flags & AHC_BIOS_ENABLED) + vlb = sort_list[0]; + else + vlb = sort_list[2]; + + if (vlb == NULL) { - if (vlb_enab == NULL) - { - vlb_enab = temp_p; - temp_p = temp_p->next; - vlb_enab->next = NULL; - } - else - { - current_p = vlb_enab; - prev_p = NULL; - while ( (current_p != NULL) && - (current_p->bios_address < temp_p->bios_address)) - { - prev_p = current_p; - current_p = current_p->next; - } - if (prev_p != NULL) - { - prev_p->next = temp_p; - temp_p = temp_p->next; - prev_p->next->next = current_p; - } - else - { - vlb_enab = temp_p; - temp_p = temp_p->next; - vlb_enab->next = current_p; - } - } + vlb = temp_p; + temp_p = temp_p->next; + vlb->next = NULL; } else { - if (vlb_disab == NULL) + current_p = vlb; + prev_p = NULL; + while ( (current_p != NULL) && + (current_p->bios_address < temp_p->bios_address)) + { + prev_p = current_p; + current_p = current_p->next; + } + if (prev_p != NULL) { - vlb_disab = temp_p; + prev_p->next = temp_p; temp_p = temp_p->next; - vlb_disab->next = NULL; + prev_p->next->next = current_p; } else { - current_p = vlb_disab; - prev_p = NULL; - while ( (current_p != NULL) && - (current_p->base < temp_p->base)) - { - prev_p = current_p; - current_p = current_p->next; - } - if (prev_p != NULL) - { - prev_p->next = temp_p; - temp_p = temp_p->next; - prev_p->next->next = current_p; - } - else - { - vlb_disab = temp_p; - temp_p = temp_p->next; - vlb_disab->next = current_p; - } + vlb = temp_p; + temp_p = temp_p->next; + vlb->next = current_p; } } + + if (p->flags & AHC_BIOS_ENABLED) + sort_list[0] = vlb; + else + sort_list[2] = vlb; + break; + } default: /* All PCI controllers fall through to default */ + { + + p = temp_p; + if (p->flags & AHC_BIOS_ENABLED) + pci = sort_list[1]; + else + pci = sort_list[3]; + if (pci == NULL) { pci = temp_p; @@ -8101,75 +9523,60 @@ pci->next = current_p; } } + + if (p->flags & AHC_BIOS_ENABLED) + sort_list[1] = pci; + else + sort_list[3] = pci; + break; + } } /* End of switch(temp_p->type) */ } /* End of while (temp_p != NULL) */ /* * At this point, the cards have been broken into 4 sorted lists, now * we run through the lists in order and register each controller */ - left = found; - temp_p = vlb_enab; - while(temp_p != NULL) - { - template->name = board_names[temp_p->board_name_index]; - p = aic7xxx_alloc(template, temp_p); - if (p != NULL) - { - p->instance = found - left; - if (aic7xxx_register(template, p, (--left)) == 0) - { - found--; - aic7xxx_free(p); - scsi_unregister(p->host); - } - } - current_p = temp_p; - temp_p = (struct aic7xxx_host *)temp_p->next; - kfree(current_p); - } - temp_p = pci; - while(temp_p != NULL) - { - template->name = board_names[temp_p->board_name_index]; - p = aic7xxx_alloc(template, temp_p); - if (p != NULL) - { - p->instance = found - left; - if (aic7xxx_register(template, p, (--left)) == 0) - { - found--; - aic7xxx_free(p); - scsi_unregister(p->host); - } - } - current_p = temp_p; - temp_p = (struct aic7xxx_host *)temp_p->next; - kfree(current_p); - } - temp_p = vlb_disab; - while(temp_p != NULL) { - template->name = board_names[temp_p->board_name_index]; - p = aic7xxx_alloc(template, temp_p); - if (p != NULL) + int i; + + left = found; + for (i=0; iinstance = found - left; - if (aic7xxx_register(template, p, (--left)) == 0) + temp_p = sort_list[i]; + while(temp_p != NULL) { - found--; - aic7xxx_free(p); - scsi_unregister(p->host); + template->name = board_names[temp_p->board_name_index]; + p = aic7xxx_alloc(template, temp_p); + if (p != NULL) + { + p->instance = found - left; + if (aic7xxx_register(template, p, (--left)) == 0) + { + found--; + aic7xxx_release(p->host); + scsi_unregister(p->host); + } + else if (aic7xxx_dump_card) + { + pause_sequencer(p); + aic7xxx_print_card(p); + aic7xxx_print_scratch_ram(p); + unpause_sequencer(p, TRUE); + } + } + current_p = temp_p; + temp_p = (struct aic7xxx_host *)temp_p->next; + kfree(current_p); } } - current_p = temp_p; - temp_p = (struct aic7xxx_host *)temp_p->next; - kfree(current_p); } } return (found); } +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS + /*+F************************************************************************* * Function: * aic7xxx_negotiation_complete @@ -8181,7 +9588,7 @@ static void aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) { - memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer)); + return; } /*+F************************************************************************* @@ -8221,10 +9628,19 @@ cmd->underflow = 0; cmd->cmd_len = 6; } + /* + * Before sending this thing out, we also amke the cmd->next pointer + * point to the real command so we can stuff any possible SENSE data + * intp the real command instead of this fake command. This has to be + * done each time the command is built, not just the first time, hence + * it's outside of the above if()... + */ + p->dev_wdtr_cmnd[tindex]->next = old_cmd; aic7xxx_queue(p->dev_wdtr_cmnd[tindex], aic7xxx_negotiation_complete); } - else if ( (p->needsdtr & (1<sdtr_pending & (1<needsdtr & (1<sdtr_pending & (1<wdtr_pending & (1<dev_sdtr_cmnd[tindex] == NULL) { @@ -8248,11 +9664,45 @@ cmd->underflow = 0; cmd->cmd_len = 6; } + /* + * Before sending this thing out, we also amke the cmd->next pointer + * point to the real command so we can stuff any possible SENSE data + * intp the real command instead of this fake command. This has to be + * done each time the command is built, not just the first time, hence + * it's outside of the above if()... + */ + p->dev_sdtr_cmnd[tindex]->next = old_cmd; aic7xxx_queue(p->dev_sdtr_cmnd[tindex], aic7xxx_negotiation_complete); } } +#endif + +#ifdef AIC7XXX_VERBOSE_DEBUGGING +/*+F************************************************************************* + * Function: + * aic7xxx_print_scb + * + * Description: + * Dump the byte codes for an about to be sent SCB. + *-F*************************************************************************/ +static void +aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) +{ + int i; + unsigned char *x; + + x = (unsigned char *)&scb->hscb->control; + + for(i=0; i<32; i++) + { + printk("%02x ", x[i]); + } + printk("\n"); +} +#endif + /*+F************************************************************************* * Function: * aic7xxx_buildscb @@ -8306,12 +9756,13 @@ } if (p->dev_flags[TARGET_INDEX(cmd)] & DEVICE_SCANNED) { +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) ) { if (cmd == p->dev_wdtr_cmnd[TARGET_INDEX(cmd)]) { p->wdtr_pending |= mask; - scb->flags |= SCB_MSGOUT_WDTR_16BIT; + scb->flags |= SCB_MSGOUT_WDTR; hscb->control &= DISCENB; hscb->control |= MK_MESSAGE; scb->tag_action = 0; @@ -8321,7 +9772,8 @@ aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd)); } } - if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) ) + else if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) && + !(p->wdtr_pending & mask) ) { if (cmd == p->dev_sdtr_cmnd[TARGET_INDEX(cmd)]) { @@ -8336,6 +9788,36 @@ aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd)); } } +#else + if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) && + !(p->sdtr_pending & mask) && (cmd->lun == 0) ) + { + p->wdtr_pending |= mask; + scb->flags |= SCB_MSGOUT_WDTR; + hscb->control &= DISCENB; + hscb->control |= MK_MESSAGE; + scb->tag_action = 0; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Building WDTR command.\n", p->host_no, + CTL_OF_CMD(cmd)); +#endif + } + else if ( (p->needsdtr & mask) && !(p->wdtr_pending & mask) && + !(p->sdtr_pending & mask) && (cmd->lun == 0) ) + { + p->sdtr_pending |= mask; + scb->flags |= SCB_MSGOUT_SDTR; + hscb->control &= DISCENB; + hscb->control |= MK_MESSAGE; + scb->tag_action = 0; +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if (aic7xxx_verbose > 0xffff) + printk(INFO_LEAD "Building SDTR command.\n", p->host_no, + CTL_OF_CMD(cmd)); +#endif + } +#endif } hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) | ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07); @@ -8368,19 +9850,26 @@ sg = (struct scatterlist *)cmd->request_buffer; scb->sg_length = 0; + /* + * Copy the segments into the SG array. NOTE!!! - We used to + * have the first entry both in the data_pointer area and the first + * SG element. That has changed somewhat. We still have the first + * entry in both places, but now we download the address of + * scb->sg_list[1] instead of 0 to the sg pointer in the hscb. + */ for (i = 0; i < cmd->use_sg; i++) { scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address)); scb->sg_list[i].length = cpu_to_le32(sg[i].length); scb->sg_length += sg[i].length; } - hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(scb->sg_list)); - hscb->SG_segment_count = cmd->use_sg; - scb->sg_count = cmd->use_sg; - /* Copy the first SG into the data pointer area. */ hscb->data_pointer = scb->sg_list[0].address; hscb->data_count = scb->sg_list[0].length; + scb->sg_count = cmd->use_sg; + hscb->SG_segment_count = cmd->use_sg; + hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[1])); + } else { @@ -8405,6 +9894,12 @@ hscb->data_pointer = 0; } } +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if((cmd->cmnd[0] == TEST_UNIT_READY) && (aic7xxx_verbose & VERBOSE_PROBE2)) + { + aic7xxx_print_scb(p, scb); + } +#endif } /*+F************************************************************************* @@ -8419,7 +9914,9 @@ { struct aic7xxx_host *p; struct aic7xxx_scb *scb; +#ifdef AIC7XXX_VERBOSE_DEBUGGING int tindex = TARGET_INDEX(cmd); +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) unsigned long cpu_flags = 0; #endif @@ -8428,18 +9925,22 @@ /* * Check to see if channel was scanned. */ + +#ifdef AIC7XXX_VERBOSE_DEBUGGING if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0)) { - printk(INFO_LEAD "Scanning channel for devices.\n", - p->host_no, 0, -1, -1); + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(INFO_LEAD "Scanning channel for devices.\n", + p->host_no, 0, -1, -1); p->flags |= AHC_A_SCANNED; } else { if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1)) { - printk(INFO_LEAD "Scanning channel for devices.\n", - p->host_no, 1, -1, -1); + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(INFO_LEAD "Scanning channel for devices.\n", + p->host_no, 1, -1, -1); p->flags |= AHC_B_SCANNED; } } @@ -8453,12 +9954,25 @@ if ( p->dev_active_cmds[tindex] > 220 ) p->dev_active_cmds[tindex] = 0; } - DRIVER_LOCK - scb = aic7xxx_allocate_scb(p, FALSE); - DRIVER_UNLOCK +#endif + + scb = scbq_remove_head(&p->scb_data->free_scbs); + if (scb == NULL) + { + DRIVER_LOCK + aic7xxx_allocate_scb(p); + DRIVER_UNLOCK + scb = scbq_remove_head(&p->scb_data->free_scbs); + } if (scb == NULL) { - panic("(scsi%d) aic7xxx_queue:Couldn't get a free SCB.\n", p->host_no); + printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no, + CTL_OF_CMD(cmd)); + cmd->result = (DID_BUS_BUSY << 16); + DRIVER_LOCK + aic7xxx_queue_cmd_complete(p, cmd); + DRIVER_UNLOCK + return 0; } else { @@ -8477,23 +9991,16 @@ * the SCB to the sequencer and watch the fun begin. */ cmd->scsi_done = fn; + cmd->result = DID_OK; + memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); aic7xxx_error(cmd) = DID_OK; aic7xxx_status(cmd) = 0; - cmd->result = 0; cmd->host_scribble = NULL; - memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer)); scb->flags |= SCB_ACTIVE | SCB_WAITINGQ; DRIVER_LOCK - if (p->delayed_scbs[tindex].head != NULL) - { - scbq_insert_tail(&p->delayed_scbs[tindex], scb); - } - else - { - scbq_insert_tail(&p->waiting_scbs, scb); - } + scbq_insert_tail(&p->waiting_scbs, scb); if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0) { aic7xxx_run_waiting_queues(p); @@ -8681,7 +10188,10 @@ printk(INFO_LEAD "Queueing device reset " "command.\n", p->host_no, CTL_OF_SCB(scb)); p->qinfifo[p->qinfifonext++] = scb->hscb->tag; - aic_outb(p, p->qinfifonext, KERNEL_QINPOS); + if (p->features & AHC_QUEUE_REGS) + aic_outb(p, p->qinfifonext, HNSCB_QOFF); + else + aic_outb(p, p->qinfifonext, KERNEL_QINPOS); scb->flags |= SCB_QUEUED_ABORT; result = SCSI_RESET_PENDING; } @@ -8702,30 +10212,142 @@ * Abort the current SCSI command(s). *-F*************************************************************************/ void -aic7xxx_panic_abort(struct aic7xxx_host *p) +aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { - int i; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) + int i, mask, found, need_tag; + struct aic7xxx_scb *scb; + unsigned char qinpos, hscbp; - printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION); + found = FALSE; +#endif + + printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION, + UTS_RELEASE); printk("Controller type:\n %s\n", board_names[p->board_name_index]); + printk("p->flags=0x%x, p->chip=0x%x, p->features=0x%x, " + "sequencer %s paused\n", + p->flags, p->chip, p->features, + (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" ); + pause_sequencer(p); + disable_irq(p->irq); + aic7xxx_print_card(p); + aic7xxx_print_scratch_ram(p); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) for(i=0; idev_flags[i] & DEVICE_PRESENT) { - printk(INFO_LEAD "dev_flags=0x%x, WDTR:%s, SDTR:%s, q_depth=%d:%d\n", + mask = (0x01 << i); + printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c," + " q_depth=%d:%d:%d\n", p->host_no, 0, i, 0, p->dev_flags[i], - (p->needwdtr_copy & (1 << i)) ? "Yes" : "No", - (p->needsdtr_copy & (1 << i)) ? "Yes" : "No", + (p->wdtr_pending & mask) ? 'Y' : 'N', + (p->needwdtr & mask) ? 'Y' : 'N', + (p->needwdtr_copy & mask) ? 'Y' : 'N', + (p->sdtr_pending & mask) ? 'Y' : 'N', + (p->needsdtr & mask) ? 'Y' : 'N', + (p->needsdtr_copy & mask) ? 'Y' : 'N', + p->dev_active_cmds[i], p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]); + printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0, + aic_inb(p, TARG_SCSIRATE + i)); + if (p->features & AHC_ULTRA2) + printk(", targ_offset=%d", aic_inb(p, TARG_OFFSET + i)); + printk("\n"); } } - printk("SIMODE0=0x%x, SIMODE1=0x%x, SSTAT0=0x%x, SSTAT1=0x%x, INTSTAT=0x%x\n", - aic_inb(p, SIMODE0), aic_inb(p, SIMODE1), aic_inb(p, SSTAT0), - aic_inb(p, SSTAT1), aic_inb(p, INTSTAT) ); - printk("p->flags=0x%x, p->type=0x%x, sequencer %s paused\n", - p->flags, p->type, - (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" ); - panic("Stopping to debug\n"); + /* + * Search for this command and see if we can't track it down, it's the + * one causing the timeout. Print out this command first, then all other + * active commands afterwords. + */ + need_tag = -1; + if ( cmd ) + { + scb = p->scb_data->scb_array[aic7xxx_position(cmd)]; + if ( (scb->flags & SCB_ACTIVE) && (scb->cmd == cmd) ) + { + printk("Timed out command is scb #%d:\n", scb->hscb->tag); + printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag, + scb->flags, scb->hscb->control, scb->hscb->target_channel_lun, + (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" ); + need_tag = scb->hscb->tag; + if (scb->flags & SCB_WAITINGQ) found=TRUE; + } + } + printk("QINFIFO: (TAG) "); + qinpos = aic_inb(p, QINPOS); + while ( qinpos != p->qinfifonext ) + { + if (p->qinfifo[qinpos] == need_tag) + found=TRUE; + printk("%d ", p->qinfifo[qinpos++]); + } + printk("\n"); + printk("Current SCB: (SCBPTR/TAG/CONTROL) %d/%d/0x%x\n", aic_inb(p, SCBPTR), + aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL) ); + if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; + printk("WAITING_SCBS: (SCBPTR/TAG/CONTROL) %d->", + hscbp = aic_inb(p, WAITING_SCBH)); + while (hscbp != SCB_LIST_NULL) + { + aic_outb(p, hscbp, SCBPTR); + printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); + hscbp = aic_inb(p, SCB_NEXT); + if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; + } + printk("\n"); + printk("DISCONNECTED_SCBS: (SCBPTR/TAG/CONTROL) %d->", + hscbp = aic_inb(p, DISCONNECTED_SCBH)); + while (hscbp != SCB_LIST_NULL) + { + aic_outb(p, hscbp, SCBPTR); + printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); + hscbp = aic_inb(p, SCB_NEXT); + if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; + } + printk("\n"); + printk("FREE_SCBS: (SCBPTR/TAG/CONTROL) %d->", + hscbp = aic_inb(p, FREE_SCBH)); + while (hscbp != SCB_LIST_NULL) + { + aic_outb(p, hscbp, SCBPTR); + printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); + hscbp = aic_inb(p, SCB_NEXT); + } + printk("\n"); + + if (found == FALSE) + { + /* + * We haven't found the offending SCB yet, and it should be around + * somewhere, so go look for it in the cards SCBs. + */ + printk("SCBPTR CONTROL TAG PREV NEXT\n"); + for(i=0; iscb_data->maxhscbs; i++) + { + aic_outb(p, i, SCBPTR); + printk(" %3d %02x %02x %02x %02x\n", i, + aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG), + aic_inb(p, SCB_PREV), aic_inb(p, SCB_NEXT)); + } + } + + + for (i=0; i < p->scb_data->numscbs; i++) + { + scb = p->scb_data->scb_array[i]; + if ( (scb->flags & SCB_ACTIVE) && (scb->cmd != cmd) ) + { + printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag, + scb->flags, scb->hscb->control, scb->hscb->target_channel_lun, + (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" ); + } + } +#endif + sti(); + for(;;) barrier(); } /*+F************************************************************************* @@ -8759,7 +10381,7 @@ * code. */ if (aic7xxx_panic_on_abort) - aic7xxx_panic_abort(p); + aic7xxx_panic_abort(p, cmd); DRIVER_LOCK @@ -8814,7 +10436,7 @@ p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble; else cmd_prev->host_scribble = cmd_next->host_scribble; - cmd_next->done(cmd_next); + cmd_next->scsi_done(cmd_next); unpause_sequencer(p, FALSE); DRIVER_UNLOCK return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful @@ -8838,7 +10460,12 @@ * command held by the scb pointer and is a valid abort request. * Now, we just have to figure out what to do from here. Current plan is: * if we have already been here on this command, escalate to a reset - * if scb is on waiting list or QINFIFO, send it back as aborted + * if scb is on waiting list or QINFIFO, send it back as aborted, but + * we also need to be aware of the possibility that we could be using + * a faked negotiation command that is holding this command up, if + * so we need to take care of that command instead, which means we + * would then treat this one like it was sitting around disconnected + * instead. * if scb is on WAITING_SCB list in sequencer, free scb and send back * if scb is disconnected and not completed, abort with abort message * if scb is currently running, then it may be causing the bus to hang @@ -8918,18 +10545,55 @@ if ((found == 0) && (scb->flags & SCB_WAITINGQ)) { - int tindex = TARGET_INDEX(cmd); - - if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) - printk(INFO_LEAD "SCB found on waiting list and " - "aborted.\n", p->host_no, CTL_OF_SCB(scb)); - scbq_remove(&p->waiting_scbs, scb); - scbq_remove(&p->delayed_scbs[tindex], scb); - p->dev_active_cmds[tindex]++; - p->activescbs++; - scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE); - scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE; + int tindex = TARGET_INDEX(cmd); +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS + unsigned short mask; + + mask = (1 << tindex); + + if (p->wdtr_pending & mask) + { + if (p->dev_wdtr_cmnd[tindex]->next != cmd) + found = 1; + else + found = 0; + } + else if (p->sdtr_pending & mask) + { + if (p->dev_sdtr_cmnd[tindex]->next != cmd) + found = 1; + else + found = 0; + } + else + { found = 1; + } + if (found == 0) + { + /* + * OK..this means the command we are currently getting an abort + * for has an outstanding negotiation command in front of it. + * We don't really have a way to tie back into the negotiation + * commands, so we just send this back as pending, then it + * will get reset in 2 seconds. + */ + unpause_sequencer(p, TRUE); + scb->flags |= SCB_ABORT; + DRIVER_UNLOCK + return(SCSI_ABORT_PENDING); + } +#endif + if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) + printk(INFO_LEAD "SCB found on waiting list and " + "aborted.\n", p->host_no, CTL_OF_SCB(scb)); + scbq_remove(&p->waiting_scbs, scb); + scbq_remove(&p->delayed_scbs[tindex], scb); + p->dev_active_cmds[tindex]++; + p->activescbs++; + scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE); + scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE; + found = 1; } /* @@ -9022,7 +10686,10 @@ printk(INFO_LEAD "SCB disconnected. Queueing Abort" " SCB.\n", p->host_no, CTL_OF_SCB(scb)); p->qinfifo[p->qinfifonext++] = scb->hscb->tag; - aic_outb(p, p->qinfifonext, KERNEL_QINPOS); + if (p->features & AHC_QUEUE_REGS) + aic_outb(p, p->qinfifonext, HNSCB_QOFF); + else + aic_outb(p, p->qinfifonext, KERNEL_QINPOS); } if (found) { @@ -9099,7 +10766,7 @@ * code. */ if (aic7xxx_panic_on_abort) - aic7xxx_panic_abort(p); + aic7xxx_panic_abort(p, cmd); DRIVER_LOCK @@ -9219,7 +10886,7 @@ "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd)); action = BUS_RESET; } - if ( (action & BUS_RESET) && !(p->type & AHC_TWIN) ) + if ( (action & BUS_RESET) && !(p->features & AHC_TWIN) ) { action = HOST_RESET; } @@ -9286,16 +10953,11 @@ p->reset_start = jiffies; p->flags |= AHC_IN_RESET; aic7xxx_reset_channel(p, cmd->channel, TRUE); - if ( (p->type & AHC_TWIN) && (action & HOST_RESET) ) + if ( (p->features & AHC_TWIN) && (action & HOST_RESET) ) { aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE); restart_sequencer(p); } - if (scb == NULL) - { - cmd->result = DID_RESET << 16; - cmd->done(cmd); - } p->last_reset = jiffies; if (action != HOST_RESET) result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET; @@ -9377,6 +11039,7 @@ if(p->irq) free_irq(p->irq, p); release_region(p->base, MAXREG - MINREG); +#ifdef MMAPIO if(p->maddr) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) @@ -9385,6 +11048,7 @@ iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK)); #endif } +#endif /* MMAPIO */ prev = NULL; next = first_aic7xxx; while(next != NULL) @@ -9405,6 +11069,207 @@ aic7xxx_free(p); return(0); } + +/*+F************************************************************************* + * Function: + * aic7xxx_print_card + * + * Description: + * Print out all of the control registers on the card + * + * NOTE: This function is not yet safe for use on the VLB and EISA + * controllers, so it isn't used on those controllers at all. + *-F*************************************************************************/ +static void +aic7xxx_print_card(struct aic7xxx_host *p) +{ + int i, j, k, chip; + static struct register_ranges { + int num_ranges; + int range_val[32]; + } cards_ds[] = { + { 0, {0,} }, /* none */ + {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/ + 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} }, + { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/ + 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} }, + { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/ + 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} }, + {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/ + 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} }, + {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/ + 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} }, + {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/ + 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f, + 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc, + 0xfe, 0xff} }, + {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/ + 0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, + 0x9f, 0x9f, 0xe0, 0xf1} }, + {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/ + 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f, + 0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc, + 0xfe, 0xff} }, + }; +#ifdef CONFIG_PCI + static struct register_ranges cards_ns[] = { + { 0, {0,} }, /* none */ + { 0, {0,} }, /* 7771 */ + { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33, + 0x3c, 0x41, 0x43, 0x47} }, + { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33, + 0x3c, 0x41, 0x43, 0x47} }, + { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x33, 0x3c, 0x41} }, + { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47} }, + { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} }, + { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47, + 0xdc, 0xe3} }, + { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3, + 0xff, 0xff} } + }; +#endif + chip = p->chip & AHC_CHIPID_MASK; + /* + * Let's run through the PCI space first.... + */ + printk("%s at ", + board_names[p->board_name_index]); + switch(p->chip & ~AHC_CHIPID_MASK) + { + case AHC_VL: + printk("VLB Slot %d.\n", p->pci_device_fn); + break; + case AHC_EISA: + printk("EISA Slot %d.\n", p->pci_device_fn); + break; + case AHC_PCI: + default: + printk("PCI %d/%d.\n", PCI_SLOT(p->pci_device_fn), + PCI_FUNC(p->pci_device_fn)); + break; + } + +#ifdef CONFIG_PCI + { + unsigned char temp; + + printk("PCI Dump:\n"); + k=0; + for(i=0; i KERNEL_VERSION(2,1,92) + pci_read_config_byte(p->pdev, j, &temp); +#else + pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, j, &temp); +#endif + printk("%02x:%02x ", j, temp); + if(++k == 13) + { + printk("\n"); + k = 0; + } + } + } + } + if(k != 0) + printk("\n"); +#endif /* CONFIG_PCI */ + + /* + * Now the registers on the card.... + */ + printk("Card Dump:\n"); + k = 0; + for(i=0; iflags & AHC_SEEPROM_FOUND) + { + unsigned short *sc1; + sc1 = (unsigned short *)&p->sc; + + printk("SEEPROM dump.\n"); + for(i=1; i<=32; i++) + { + printk("0x%04x", sc1[i-1]); + if ( (i % 8) == 0 ) + printk("\n"); + else + printk(" "); + } + } + + /* + * If this was an Ultra2 controller, then we just hosed the card in terms + * of the QUEUE REGS. This function is only called at init time or by + * the panic_abort function, so it's safe to assume a generic init time + * setting here + */ + + if(p->features & AHC_QUEUE_REGS) + { + aic_outb(p, 0, SDSCB_QOFF); + aic_outb(p, 0, SNSCB_QOFF); + aic_outb(p, 0, HNSCB_QOFF); + } + +} + +/*+F************************************************************************* + * Function: + * aic7xxx_print_scratch_ram + * + * Description: + * Print out the scratch RAM values on the card. + *-F*************************************************************************/ +static void +aic7xxx_print_scratch_ram(struct aic7xxx_host *p) +{ + int i, k; + + k = 0; + printk("Scratch RAM:\n"); + for(i = SRAM_BASE; i < SEQCTL; i++) + { + printk("%02x:%02x ", i, aic_inb(p, i)); + if(++k == 13) + { + printk("\n"); + k=0; + } + } + if (p->features & AHC_MORE_SRAM) + { + for(i = TARG_OFFSET; i < 0x80; i++) + { + printk("%02x:%02x ", i, aic_inb(p, i)); + if(++k == 13) + { + printk("\n"); + k=0; + } + } + } + printk("\n"); +} + #include "aic7xxx_proc.c" diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c --- v2.0.35/linux/drivers/scsi/aic7xxx_proc.c Sun Nov 15 10:49:44 1998 +++ linux/drivers/scsi/aic7xxx_proc.c Sun Nov 15 10:33:08 1998 @@ -85,10 +85,8 @@ struct aic7xxx_host *p; int size = 0; unsigned char i; -#ifdef AIC7XXX_PROC_STATS struct aic7xxx_xferstats *sp; unsigned char target, lun; -#endif HBAptr = NULL; @@ -130,16 +128,18 @@ */ size = 4096; -#ifdef AIC7XXX_PROC_STATS for (target = 0; target < MAX_TARGETS; target++) { for (lun = 0; lun < MAX_LUNS; lun++) { if (p->stats[target][lun].xfers != 0) +#ifdef AIC7XXX_PROC_STATS size += 512; +#else + size += 256; +#endif } } -#endif if (aic7xxx_buffer_size != size) { if (aic7xxx_buffer != NULL) @@ -195,9 +195,11 @@ if (p->flags & (AHC_CHNLB|AHC_CHNLC)) channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C"; } - if (p->type & AHC_WIDE) + if (p->features & AHC_WIDE) wide = "Wide "; - if (p->type & AHC_ULTRA) + if (p->features & AHC_ULTRA2) + ultra = "Ultra2-LVD/SE "; + else if (p->features & AHC_ULTRA) ultra = "Ultra "; size += sprintf(BLS, " %s%sController%s\n", ultra, wide, channel); @@ -210,25 +212,26 @@ { size += sprintf(BLS, " PCI MMAPed I/O Base: 0x%lx\n", p->mbase); } - if( !(p->type & AHC_AIC78x0) ) + if( (p->chip & (AHC_VL | AHC_EISA)) ) { size += sprintf(BLS, " BIOS Memory Address: 0x%08x\n", p->bios_address); - size += sprintf(BLS, " %s\n", - (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled"); - } - else - { - size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n", - (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled"); } + size += sprintf(BLS, " Adapter SEEPROM Config: %s\n", + (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." : + ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." : + "SEEPROM not found, using leftover BIOS values.") ); + size += sprintf(BLS, " Adaptec SCSI BIOS: %s\n", + (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled"); size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq); size += sprintf(BLS, " SCBs: Active %d, Max Active %d,\n", p->activescbs, p->max_activescbs); size += sprintf(BLS, " Allocated %d, HW %d, " "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs, p->scb_data->maxscbs); + if (p->flags & AHC_EXTERNAL_SRAM) + size += sprintf(BLS, " Using External SCB SRAM\n"); size += sprintf(BLS, " Interrupts: %ld", p->isr_count); - if (p->type & AHC_AIC7770) + if (p->chip & AHC_EISA) { size += sprintf(BLS, " %s\n", (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)"); @@ -244,7 +247,7 @@ size += sprintf(BLS, " Extended Translation: %sabled\n", (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis"); size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable); - if (p->type & AHC_ULTRA) + if (p->features & (AHC_ULTRA | AHC_ULTRA2)) { size += sprintf(BLS, " Ultra Enable Flags: 0x%04x\n", p->ultraenb); } @@ -268,7 +271,6 @@ size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]); size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]); -#ifdef AIC7XXX_PROC_STATS size += sprintf(BLS, "\n"); size += sprintf(BLS, "Statistics:\n"); for (target = 0; target < MAX_TARGETS; target++) @@ -280,7 +282,7 @@ { continue; } - if (p->type & AHC_TWIN) + if (p->features & AHC_TWIN) { size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", p->host_no, (target >> 3), (target & 0x7), lun); @@ -290,10 +292,50 @@ size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n", p->host_no, 0, target, lun); } - size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n", + size += sprintf(BLS, " Device using %s/%s\n", + (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ? + "Wide" : "Narrow", + (p->transinfo[target].cur_offset != 0) ? + "Sync transfers at" : "Async transfers." ); + if (p->transinfo[target].cur_offset != 0) + { + struct aic7xxx_syncrate *sync_rate; + int period = p->transinfo[target].cur_period; + int rate = (p->transinfo[target].cur_width == + MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0; + + sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2); + if (sync_rate != NULL) + { + size += sprintf(BLS, " %s MByte/sec, offset %d\n", + sync_rate->rate[rate], + p->transinfo[target].cur_offset ); + } + else + { + size += sprintf(BLS, " 3.3 MByte/sec, offset %d\n", + p->transinfo[target].cur_offset ); + } + } + size += sprintf(BLS, " Device Negotiation Settings\n"); + size += sprintf(BLS, " Period Offset Bus Width\n"); + size += sprintf(BLS, "User %03d %03d %d\n", + p->transinfo[target].user_period, + p->transinfo[target].user_offset, + p->transinfo[target].user_width); + size += sprintf(BLS, "Goal %03d %03d %d\n", + p->transinfo[target].goal_period, + p->transinfo[target].goal_offset, + p->transinfo[target].goal_width); + size += sprintf(BLS, "Current %03d %03d %d\n", + p->transinfo[target].cur_period, + p->transinfo[target].cur_offset, + p->transinfo[target].cur_width); + size += sprintf(BLS, " Total transfers %ld (%ld read;%ld written)\n", sp->xfers, sp->r_total, sp->w_total); - size += sprintf(BLS, "blks(512) rd=%ld; blks(512) wr=%ld\n", + size += sprintf(BLS, " blks(512) rd=%ld; blks(512) wr=%ld\n", sp->r_total512, sp->w_total512); +#ifdef AIC7XXX_PROC_STATS size += sprintf(BLS, "%s\n", HDRB); size += sprintf(BLS, " Reads:"); for (i = 0; i < NUMBER(sp->r_bins); i++) @@ -306,10 +348,10 @@ { size += sprintf(BLS, "%6ld ", sp->w_bins[i]); } +#endif /* AIC7XXX_PROC_STATS */ size += sprintf(BLS, "\n\n"); } } -#endif /* AIC7XXX_PROC_STATS */ if (size >= aic7xxx_buffer_size) { diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h --- v2.0.35/linux/drivers/scsi/aic7xxx_reg.h Mon Jul 13 13:46:35 1998 +++ linux/drivers/scsi/aic7xxx_reg.h Sun Nov 15 10:33:08 1998 @@ -49,11 +49,13 @@ #define SCSIRATE 0x04 #define WIDEXFER 0x80 +#define SXFR_ULTRA2 0x7f #define SXFR 0x70 #define SOFS 0x0f #define SCSIID 0x05 -#define OID 0x0f +#define SCSIOFFSET 0x05 +#define SOFS_ULTRA2 0x7f #define SCSIDATL 0x06 @@ -73,6 +75,7 @@ #define SELDO 0x40 #define SELDI 0x20 #define SELINGO 0x10 +#define IOERR 0x08 #define SWRAP 0x08 #define SDONE 0x04 #define SPIORDY 0x02 @@ -100,20 +103,20 @@ #define SSTAT2 0x0d #define OVERRUN 0x80 #define SFCNT 0x1f +#define EXP_ACTIVE 0x10 #define SSTAT3 0x0e #define SCSICNT 0xf0 #define OFFCNT 0x0f -#define SCSITEST 0x0f -#define RQAKCNT 0x04 -#define CNTRTEST 0x02 -#define CMODE 0x01 +#define SCSIID_ULTRA2 0x0f +#define OID 0x0f #define SIMODE0 0x10 #define ENSELDO 0x40 #define ENSELDI 0x20 #define ENSELINGO 0x10 +#define ENIOERR 0x08 #define ENSWRAP 0x08 #define ENSDONE 0x04 #define ENSPIORDY 0x02 @@ -161,10 +164,15 @@ #define BRDDAT7 0x80 #define BRDDAT6 0x40 #define BRDDAT5 0x20 +#define BRDDAT4 0x10 #define BRDSTB 0x10 #define BRDCS 0x08 +#define BRDDAT3 0x08 +#define BRDDAT2 0x04 #define BRDRW 0x04 +#define BRDRW_ULTRA2 0x02 #define BRDCTL1 0x02 +#define BRDSTB_ULTRA2 0x01 #define BRDCTL0 0x01 #define SEECTL 0x1e @@ -181,11 +189,14 @@ #define DIAGLEDEN 0x80 #define DIAGLEDON 0x40 #define AUTOFLUSHDIS 0x20 +#define ENAB40 0x08 +#define ENAB20 0x04 #define SELWIDE 0x02 +#define XCVR 0x01 #define SRAM_BASE 0x20 -#define TARG_SCRATCH 0x20 +#define TARG_SCSIRATE 0x20 #define ULTRA_ENB 0x30 @@ -194,6 +205,7 @@ #define MSG_OUT 0x34 #define DMAPARAMS 0x35 +#define PRELOADEN 0x80 #define WIDEODD 0x40 #define SCSIEN 0x20 #define SDMAENACK 0x10 @@ -258,7 +270,12 @@ #define SEND_REJ 0x20 #define MSGOUT_PHASEMIS 0x10 -#define LAST_MSG 0x52 +#define ARG_2 0x52 +#define RETURN_2 0x52 + +#define LAST_MSG 0x53 + +#define PREFETCH_CNT 0x54 #define SCSICONF 0x5a #define TERM_ENB 0x80 @@ -314,10 +331,18 @@ #define STACK 0x6f +#define TARG_OFFSET 0x70 + #define BCTL 0x84 #define ACE 0x08 #define ENABLE 0x01 +#define DSCOMMAND0 0x84 +#define INTSCBRAMSEL 0x08 +#define RAMPS 0x04 +#define USCBSIZE32 0x02 +#define CIOPARCKEN 0x01 + #define DSCOMMAND 0x84 #define CACHETHEN 0x80 #define DPARCKEN 0x40 @@ -329,12 +354,12 @@ #define BON 0x0f #define BUSSPD 0x86 -#define DFTHRSH_100 0xc0 #define DFTHRSH 0xc0 #define STBOFF 0x38 #define STBON 0x07 #define DSPCISTATUS 0x86 +#define DFTHRSH_100 0xc0 #define HCNTRL 0x87 #define POWRDN 0x40 @@ -381,6 +406,7 @@ #define CLRSEQINT 0x01 #define ERROR 0x92 +#define CIOPARERR 0x80 #define PCIERRSTAT 0x40 #define MPARERR 0x20 #define DPARERR 0x10 @@ -392,6 +418,7 @@ #define DFCNTRL 0x93 #define DFSTATUS 0x94 +#define PRELOAD_AVAIL 0x80 #define DWORDEMP 0x20 #define MREQPEND 0x10 #define HDONE 0x08 @@ -413,6 +440,8 @@ #define QOUTCNT 0x9e +#define SFUNCT 0x9f + #define SCB_CONTROL 0xa0 #define MK_MESSAGE 0x80 #define DISCENB 0x40 @@ -464,25 +493,95 @@ #define ADSEL 0x1e #define DI_2840 0x01 +#define CCHADDR 0xe0 + +#define CCHCNT 0xe8 + +#define CCSGRAM 0xe9 + +#define CCSGADDR 0xea + +#define CCSGCTL 0xeb +#define CCSGDONE 0x80 +#define CCSGEN 0x08 +#define FLAG 0x02 +#define CCSGRESET 0x01 + +#define CCSCBRAM 0xec + +#define CCSCBADDR 0xed + +#define CCSCBCTL 0xee +#define CCSCBDONE 0x80 +#define ARRDONE 0x40 +#define CCARREN 0x10 +#define CCSCBEN 0x08 +#define CCSCBDIR 0x04 +#define CCSCBRESET 0x01 + +#define CCSCBCNT 0xef + +#define CCSCBPTR 0xf1 + +#define HNSCB_QOFF 0xf4 + +#define SNSCB_QOFF 0xf6 + +#define SDSCB_QOFF 0xf8 + +#define QOFF_CTLSTA 0xfa +#define SCB_AVAIL 0x40 +#define SNSCB_ROLLOVER 0x20 +#define SDSCB_ROLLOVER 0x10 +#define SCB_QSIZE 0x07 +#define SCB_QSIZE_256 0x06 + +#define DFF_THRSH 0xfb +#define WR_DFTHRSH 0x70 +#define WR_DFTHRSH_MAX 0x70 +#define WR_DFTHRSH_90 0x60 +#define WR_DFTHRSH_85 0x50 +#define WR_DFTHRSH_75 0x40 +#define WR_DFTHRSH_63 0x30 +#define WR_DFTHRSH_50 0x20 +#define WR_DFTHRSH_25 0x10 +#define RD_DFTHRSH_MAX 0x07 +#define RD_DFTHRSH 0x07 +#define RD_DFTHRSH_90 0x06 +#define RD_DFTHRSH_85 0x05 +#define RD_DFTHRSH_75 0x04 +#define RD_DFTHRSH_63 0x03 +#define RD_DFTHRSH_50 0x02 +#define RD_DFTHRSH_25 0x01 +#define WR_DFTHRSH_MIN 0x00 +#define RD_DFTHRSH_MIN 0x00 + +#define SG_CACHEPTR 0xfc +#define SG_USER_DATA 0xfc +#define LAST_SEG 0x02 +#define LAST_SEG_DONE 0x01 + -#define CMD_GROUP_CODE_SHIFT 0x05 -#define BUS_8_BIT 0x00 -#define QOUTFIFO_OFFSET 0x01 #define CMD_GROUP2_BYTE_DELTA 0xfa #define MAX_OFFSET_8BIT 0x0f #define BUS_16_BIT 0x01 #define QINFIFO_OFFSET 0x02 #define CMD_GROUP5_BYTE_DELTA 0x0b +#define CMD_GROUP_CODE_SHIFT 0x05 +#define MAX_OFFSET_ULTRA2 0x7f #define MAX_OFFSET_16BIT 0x08 +#define BUS_8_BIT 0x00 +#define QOUTFIFO_OFFSET 0x01 #define UNTAGGEDSCB_OFFSET 0x00 +#define CCSGRAM_MAXSEGS 0x10 #define SCB_LIST_NULL 0xff #define SG_SIZEOF 0x08 #define CMD_GROUP4_BYTE_DELTA 0x04 #define CMD_GROUP0_BYTE_DELTA 0xfc #define HOST_MSG 0xff #define BUS_32_BIT 0x02 +#define CCSGADDR_MAX 0x80 /* Downloaded Constant Definitions */ -#define TMODE_NUMCMDS 0x01 -#define QCNTMASK 0x00 +#define TMODE_NUMCMDS 0x00 diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx_seq.c linux/drivers/scsi/aic7xxx_seq.c --- v2.0.35/linux/drivers/scsi/aic7xxx_seq.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/aic7xxx_seq.c Sun Nov 15 10:33:08 1998 @@ -0,0 +1,769 @@ +/* + * DO NOT EDIT - This file is automatically generated. + */ +static unsigned char seqprog[] = { + 0xff, 0x6a, 0x06, 0x08, + 0x32, 0x6a, 0x00, 0x00, + 0x12, 0x6a, 0x00, 0x00, + 0xff, 0x6a, 0xd6, 0x09, + 0xff, 0x6a, 0xdc, 0x09, + 0x00, 0x65, 0x38, 0x59, + 0xf7, 0x01, 0x02, 0x08, + 0xff, 0x4e, 0xc8, 0x08, + 0xbf, 0x60, 0xc0, 0x08, + 0x60, 0x0b, 0x7c, 0x68, + 0x40, 0x00, 0x0e, 0x68, + 0x08, 0x1f, 0x3e, 0x10, + 0x60, 0x0b, 0x7c, 0x68, + 0x40, 0x00, 0x0e, 0x68, + 0x08, 0x1f, 0x3e, 0x10, + 0xff, 0x3e, 0x3e, 0x60, + 0x40, 0xfa, 0x10, 0x78, + 0xff, 0xf6, 0xd4, 0x08, + 0x01, 0x4e, 0x9c, 0x18, + 0x40, 0x60, 0xc0, 0x00, + 0x00, 0x4d, 0x10, 0x70, + 0x01, 0x4e, 0x9c, 0x18, + 0xbf, 0x60, 0xc0, 0x08, + 0x00, 0x6a, 0x72, 0x5c, + 0xff, 0x4e, 0xc8, 0x18, + 0x02, 0x6a, 0x88, 0x5b, + 0xff, 0x52, 0x20, 0x09, + 0x0d, 0x6a, 0x6a, 0x00, + 0x00, 0x52, 0xfe, 0x5b, + 0xff, 0x3e, 0x74, 0x09, + 0xff, 0x90, 0x7c, 0x08, + 0xff, 0x3e, 0x20, 0x09, + 0x00, 0x65, 0x44, 0x58, + 0x00, 0x65, 0x0e, 0x40, + 0xf7, 0x1f, 0xca, 0x08, + 0x08, 0xa1, 0xc8, 0x08, + 0x00, 0x65, 0xca, 0x00, + 0xff, 0x65, 0x3e, 0x08, + 0xf0, 0xa1, 0xc8, 0x08, + 0x0f, 0x0f, 0x1e, 0x08, + 0x00, 0x0f, 0x1e, 0x00, + 0xf0, 0xa1, 0xc8, 0x08, + 0x0f, 0x05, 0x0a, 0x08, + 0x00, 0x05, 0x0a, 0x00, + 0x5a, 0x6a, 0x00, 0x04, + 0x12, 0x65, 0xc8, 0x00, + 0x00, 0x01, 0x02, 0x00, + 0x31, 0x6a, 0xca, 0x00, + 0x80, 0x37, 0x64, 0x68, + 0xff, 0x65, 0xca, 0x18, + 0xff, 0x37, 0xdc, 0x08, + 0xff, 0x6e, 0xc8, 0x08, + 0x00, 0x6c, 0x6c, 0x78, + 0x20, 0x01, 0x02, 0x00, + 0x4c, 0x37, 0xc8, 0x28, + 0x08, 0x1f, 0x74, 0x78, + 0x08, 0x37, 0x6e, 0x00, + 0x08, 0x64, 0xc8, 0x00, + 0x70, 0x64, 0xca, 0x18, + 0xff, 0x6c, 0x0a, 0x08, + 0x20, 0x64, 0xca, 0x18, + 0xff, 0x6c, 0x08, 0x0c, + 0x40, 0x0b, 0x04, 0x69, + 0x80, 0x0b, 0xf6, 0x78, + 0xa4, 0x6a, 0x06, 0x00, + 0x40, 0x6a, 0x16, 0x00, + 0x10, 0x03, 0xf2, 0x78, + 0xff, 0x50, 0xc8, 0x08, + 0x88, 0x6a, 0xcc, 0x00, + 0x49, 0x6a, 0xee, 0x5b, + 0x01, 0x6a, 0x26, 0x01, + 0xff, 0x6a, 0xca, 0x08, + 0x08, 0x01, 0x02, 0x00, + 0x02, 0x0b, 0x92, 0x78, + 0xf7, 0x01, 0x02, 0x08, + 0xff, 0x06, 0xcc, 0x08, + 0xff, 0x66, 0x32, 0x09, + 0x01, 0x65, 0xca, 0x18, + 0x80, 0x66, 0xa0, 0x78, + 0xff, 0x66, 0xa2, 0x08, + 0x10, 0x03, 0x90, 0x68, + 0xfc, 0x65, 0xc8, 0x18, + 0x00, 0x65, 0xa8, 0x48, + 0xff, 0x6a, 0x32, 0x01, + 0x01, 0x64, 0x18, 0x19, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x84, 0x6a, 0x06, 0x00, + 0x08, 0x01, 0x02, 0x00, + 0x02, 0x0b, 0xb2, 0x78, + 0xff, 0x06, 0xc8, 0x08, + 0xff, 0x64, 0x32, 0x09, + 0xff, 0x6a, 0xca, 0x08, + 0x5b, 0x64, 0xc8, 0x28, + 0x00, 0x62, 0xc4, 0x18, + 0xfc, 0x65, 0xca, 0x18, + 0xff, 0x6a, 0xd4, 0x08, + 0xfa, 0x65, 0xca, 0x18, + 0xff, 0x6a, 0xd4, 0x08, + 0x04, 0x65, 0xca, 0x18, + 0x0b, 0x65, 0xca, 0x18, + 0xff, 0x65, 0xc8, 0x08, + 0x00, 0x8c, 0x18, 0x19, + 0x02, 0x0b, 0xce, 0x78, + 0x01, 0x65, 0xd4, 0x60, + 0xf7, 0x01, 0x02, 0x08, + 0xff, 0x06, 0x32, 0x09, + 0xff, 0x65, 0xca, 0x18, + 0xff, 0x65, 0xce, 0x68, + 0x0a, 0x93, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x5c, + 0x40, 0x51, 0xe6, 0x78, + 0xe4, 0x6a, 0x06, 0x00, + 0x08, 0x01, 0x02, 0x00, + 0x04, 0x6a, 0x18, 0x5b, + 0x01, 0x50, 0xa0, 0x18, + 0x00, 0x50, 0xec, 0xe0, + 0xff, 0x6a, 0xa0, 0x08, + 0xff, 0x6a, 0x3a, 0x01, + 0x02, 0x6a, 0x22, 0x01, + 0x40, 0x51, 0xf2, 0x68, + 0xff, 0x6a, 0x06, 0x08, + 0x00, 0x65, 0x0e, 0x40, + 0x20, 0x6a, 0x16, 0x00, + 0xf0, 0x19, 0x6e, 0x08, + 0x08, 0x6a, 0x18, 0x00, + 0x08, 0x11, 0x22, 0x00, + 0x08, 0x6a, 0x5a, 0x58, + 0x08, 0x6a, 0x68, 0x00, + 0x00, 0x65, 0x18, 0x41, + 0x12, 0x6a, 0x00, 0x00, + 0x40, 0x6a, 0x16, 0x00, + 0xff, 0x3e, 0x20, 0x09, + 0xff, 0xba, 0x7c, 0x08, + 0xff, 0xa1, 0x6e, 0x08, + 0x08, 0x6a, 0x18, 0x00, + 0x08, 0x11, 0x22, 0x00, + 0x08, 0x6a, 0x5a, 0x58, + 0x80, 0x6a, 0x68, 0x00, + 0x80, 0x36, 0x6c, 0x00, + 0x00, 0x65, 0xd2, 0x5b, + 0xff, 0x3d, 0xc8, 0x08, + 0xbf, 0x64, 0x48, 0x79, + 0x80, 0x64, 0xf0, 0x71, + 0xa0, 0x64, 0x0e, 0x72, + 0xc0, 0x64, 0x08, 0x72, + 0xe0, 0x64, 0x52, 0x72, + 0x01, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x18, 0x41, + 0xf7, 0x11, 0x22, 0x08, + 0x00, 0x65, 0x38, 0x59, + 0xff, 0x06, 0xd4, 0x08, + 0xf7, 0x01, 0x02, 0x08, + 0x09, 0x0c, 0x32, 0x79, + 0x08, 0x0c, 0x0e, 0x68, + 0x01, 0x6a, 0x22, 0x01, + 0xff, 0x6a, 0x26, 0x09, + 0xff, 0x6a, 0x08, 0x08, + 0xdf, 0x01, 0x02, 0x08, + 0x01, 0x6a, 0x7a, 0x00, + 0x03, 0x36, 0x6c, 0x0c, + 0x08, 0x6a, 0xcc, 0x00, + 0xa9, 0x6a, 0xe8, 0x5b, + 0x00, 0x65, 0x66, 0x41, + 0xa8, 0x6a, 0x6a, 0x00, + 0x79, 0x6a, 0x6a, 0x00, + 0x40, 0x3d, 0x50, 0x69, + 0x04, 0x35, 0x6a, 0x00, + 0x00, 0x65, 0x3a, 0x5b, + 0x80, 0x6a, 0xd4, 0x01, + 0x10, 0x36, 0x42, 0x69, + 0x10, 0x36, 0x6c, 0x00, + 0x07, 0xac, 0x10, 0x31, + 0x88, 0x6a, 0xcc, 0x00, + 0xac, 0x6a, 0xe0, 0x5b, + 0x00, 0x65, 0xda, 0x5b, + 0xff, 0xa3, 0x70, 0x08, + 0x39, 0x6a, 0xcc, 0x00, + 0xa4, 0x6a, 0xe6, 0x5b, + 0xff, 0x38, 0x74, 0x69, + 0x80, 0x02, 0x04, 0x00, + 0xe7, 0x35, 0x6a, 0x08, + 0x03, 0x69, 0x18, 0x31, + 0xff, 0x6a, 0x10, 0x00, + 0xff, 0x6a, 0x12, 0x00, + 0xff, 0x6a, 0x14, 0x00, + 0x01, 0x38, 0x7a, 0x61, + 0x02, 0xfc, 0xf8, 0x01, + 0xbf, 0x35, 0x6a, 0x08, + 0xff, 0x69, 0xca, 0x08, + 0xff, 0x35, 0x26, 0x09, + 0x04, 0x0b, 0x7e, 0x69, + 0x04, 0x0b, 0x8a, 0x69, + 0x10, 0x0c, 0x80, 0x79, + 0x04, 0x0b, 0x88, 0x69, + 0xff, 0x6a, 0xca, 0x08, + 0x00, 0x35, 0x22, 0x5b, + 0x80, 0x02, 0xd6, 0x69, + 0xff, 0x65, 0xc8, 0x79, + 0xff, 0x38, 0x70, 0x18, + 0xff, 0x38, 0xc8, 0x79, + 0x80, 0xea, 0xaa, 0x61, + 0xef, 0x38, 0xc8, 0x18, + 0x80, 0x6a, 0xc8, 0x00, + 0x00, 0x65, 0x9c, 0x49, + 0x33, 0x38, 0xc8, 0x28, + 0xff, 0x64, 0xd0, 0x09, + 0x04, 0x39, 0xc0, 0x31, + 0x09, 0x6a, 0xd6, 0x01, + 0x80, 0xeb, 0xa2, 0x79, + 0xf7, 0xeb, 0xd6, 0x09, + 0x08, 0xeb, 0xa6, 0x69, + 0x01, 0x6a, 0xd6, 0x01, + 0x08, 0xe9, 0x10, 0x31, + 0x88, 0x6a, 0xcc, 0x00, + 0x39, 0x6a, 0xe6, 0x5b, + 0x08, 0x6a, 0x18, 0x01, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x0d, 0x93, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x5c, + 0x88, 0x6a, 0x54, 0x5c, + 0x00, 0x65, 0xda, 0x5b, + 0xff, 0x6a, 0xc8, 0x08, + 0x08, 0x39, 0x72, 0x18, + 0x00, 0x3a, 0x74, 0x20, + 0x10, 0x0c, 0x66, 0x79, + 0x80, 0x93, 0x26, 0x01, + 0x00, 0x65, 0xe0, 0x59, + 0xff, 0x08, 0x52, 0x09, + 0xff, 0x09, 0x54, 0x09, + 0xff, 0x0a, 0x56, 0x09, + 0xff, 0x38, 0x50, 0x09, + 0x12, 0x01, 0x02, 0x00, + 0x00, 0x65, 0x18, 0x41, + 0x00, 0x65, 0xe0, 0x59, + 0x12, 0x01, 0x02, 0x00, + 0x7f, 0x02, 0x04, 0x08, + 0xe1, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x18, 0x41, + 0x04, 0x93, 0xea, 0x69, + 0xdf, 0x93, 0x26, 0x09, + 0x20, 0x93, 0xe4, 0x69, + 0x02, 0x93, 0x26, 0x01, + 0x01, 0x94, 0xe6, 0x79, + 0xd7, 0x93, 0x26, 0x09, + 0x08, 0x93, 0xec, 0x69, + 0xff, 0x6a, 0xd4, 0x0c, + 0x00, 0x65, 0x3a, 0x5b, + 0x02, 0xfc, 0xf8, 0x01, + 0x05, 0xb4, 0x10, 0x31, + 0x02, 0x6a, 0x1a, 0x31, + 0x88, 0x6a, 0xcc, 0x00, + 0xb4, 0x6a, 0xe4, 0x5b, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x00, 0x65, 0xda, 0x5b, + 0x3d, 0x6a, 0x22, 0x5b, + 0xac, 0x6a, 0x22, 0x5b, + 0x00, 0x65, 0x18, 0x41, + 0x00, 0x65, 0x3a, 0x5b, + 0xff, 0x06, 0x44, 0x09, + 0x00, 0x65, 0x18, 0x41, + 0xff, 0x34, 0xca, 0x08, + 0x80, 0x65, 0x32, 0x62, + 0x0f, 0xa1, 0xca, 0x08, + 0x07, 0xa1, 0xca, 0x08, + 0x40, 0xa0, 0xc8, 0x08, + 0x00, 0x65, 0xca, 0x00, + 0x80, 0x65, 0xca, 0x00, + 0x80, 0xa0, 0x22, 0x7a, + 0xff, 0x65, 0x0c, 0x08, + 0x00, 0x65, 0x34, 0x42, + 0x20, 0xa0, 0x3a, 0x7a, + 0xff, 0x65, 0x0c, 0x08, + 0x00, 0x65, 0xd2, 0x5b, + 0xa0, 0x3d, 0x46, 0x62, + 0x23, 0xa0, 0x0c, 0x08, + 0x00, 0x65, 0xd2, 0x5b, + 0xa0, 0x3d, 0x46, 0x62, + 0x00, 0xb9, 0x3a, 0x42, + 0xff, 0x65, 0x3a, 0x62, + 0xa1, 0x6a, 0x22, 0x01, + 0xff, 0x6a, 0xd4, 0x08, + 0x10, 0x51, 0x46, 0x72, + 0x40, 0x6a, 0x18, 0x00, + 0xff, 0x65, 0x0c, 0x08, + 0x00, 0x65, 0xd2, 0x5b, + 0xa0, 0x3d, 0x46, 0x62, + 0x10, 0x3d, 0x06, 0x00, + 0x00, 0x65, 0x0e, 0x42, + 0x40, 0x6a, 0x18, 0x00, + 0xff, 0x34, 0xa6, 0x08, + 0x80, 0x34, 0x4e, 0x62, + 0x7f, 0xa0, 0x40, 0x09, + 0x08, 0x6a, 0x68, 0x00, + 0x00, 0x65, 0x18, 0x41, + 0x64, 0x6a, 0x12, 0x5b, + 0x80, 0x64, 0xbe, 0x6a, + 0x04, 0x64, 0xa4, 0x72, + 0x02, 0x64, 0xaa, 0x72, + 0x00, 0x6a, 0x6c, 0x72, + 0x03, 0x64, 0xba, 0x72, + 0x01, 0x64, 0xa0, 0x72, + 0x07, 0x64, 0x00, 0x73, + 0x08, 0x64, 0x68, 0x72, + 0x11, 0x6a, 0x22, 0x01, + 0x07, 0x6a, 0x04, 0x5b, + 0xff, 0x06, 0xd4, 0x08, + 0x00, 0x65, 0x18, 0x41, + 0xff, 0xa8, 0x70, 0x6a, + 0xff, 0xa2, 0x88, 0x7a, + 0x01, 0x6a, 0x6a, 0x00, + 0x00, 0xb9, 0xfe, 0x5b, + 0xff, 0xa2, 0x88, 0x7a, + 0x71, 0x6a, 0x22, 0x01, + 0xff, 0x6a, 0xd4, 0x08, + 0x40, 0x51, 0x88, 0x62, + 0x0d, 0x6a, 0x6a, 0x00, + 0x00, 0xb9, 0xfe, 0x5b, + 0xff, 0x3e, 0x74, 0x09, + 0xff, 0x90, 0x7c, 0x08, + 0x00, 0x65, 0x44, 0x58, + 0x00, 0x65, 0x2a, 0x41, + 0x20, 0xa0, 0x90, 0x6a, + 0xff, 0x37, 0xc8, 0x08, + 0x00, 0x6a, 0xa8, 0x5b, + 0xff, 0x6a, 0xbe, 0x5b, + 0xff, 0xf8, 0xc8, 0x08, + 0xff, 0x4f, 0xc8, 0x08, + 0x01, 0x6a, 0xa8, 0x5b, + 0x00, 0xb9, 0xbe, 0x5b, + 0x01, 0x4f, 0x9e, 0x18, + 0x02, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x6c, 0x5c, + 0x00, 0x65, 0x2a, 0x41, + 0x41, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x18, 0x41, + 0x04, 0xa0, 0x40, 0x01, + 0x00, 0x65, 0x84, 0x5c, + 0x00, 0x65, 0x2a, 0x41, + 0x10, 0x36, 0x68, 0x7a, + 0xff, 0x38, 0x46, 0x09, + 0xa4, 0x6a, 0xcc, 0x00, + 0x39, 0x6a, 0xe6, 0x5b, + 0xac, 0x6a, 0xcc, 0x00, + 0x14, 0x6a, 0xe6, 0x5b, + 0xa9, 0x6a, 0xe8, 0x5b, + 0x00, 0x65, 0x68, 0x42, + 0xef, 0x36, 0x6c, 0x08, + 0x00, 0x65, 0x68, 0x42, + 0x0f, 0x64, 0xc8, 0x08, + 0x07, 0x64, 0xc8, 0x08, + 0x00, 0x37, 0x6e, 0x00, + 0x00, 0x65, 0x78, 0x5b, + 0xff, 0x51, 0xce, 0x72, + 0x20, 0x36, 0xde, 0x7a, + 0x00, 0x90, 0x5c, 0x5b, + 0x00, 0x65, 0xe0, 0x42, + 0xff, 0x06, 0xd4, 0x08, + 0x00, 0x65, 0xd2, 0x5b, + 0xe0, 0x3d, 0xfa, 0x62, + 0x20, 0x12, 0xfa, 0x62, + 0x51, 0x6a, 0x08, 0x5b, + 0xff, 0x51, 0x20, 0x09, + 0x20, 0xa0, 0xfa, 0x7a, + 0x00, 0x90, 0x5c, 0x5b, + 0x00, 0x65, 0x56, 0x5b, + 0xff, 0x37, 0xc8, 0x08, + 0x00, 0xa1, 0xf2, 0x62, + 0x04, 0xa0, 0xf2, 0x7a, + 0xfb, 0xa0, 0x40, 0x09, + 0x80, 0x36, 0x6c, 0x00, + 0x80, 0xa0, 0x68, 0x7a, + 0x7f, 0xa0, 0x40, 0x09, + 0xff, 0x6a, 0x04, 0x5b, + 0x00, 0x65, 0x68, 0x42, + 0x04, 0xa0, 0xf8, 0x7a, + 0x00, 0x65, 0x84, 0x5c, + 0x00, 0x65, 0xfa, 0x42, + 0x00, 0x65, 0x6c, 0x5c, + 0x31, 0x6a, 0x22, 0x01, + 0x0c, 0x6a, 0x04, 0x5b, + 0x00, 0x65, 0x68, 0x42, + 0x61, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x68, 0x42, + 0x10, 0x3d, 0x06, 0x00, + 0xff, 0x65, 0x68, 0x0c, + 0xff, 0x06, 0xd4, 0x08, + 0x01, 0x0c, 0x0a, 0x7b, + 0x04, 0x0c, 0x0a, 0x6b, + 0xe0, 0x03, 0x7a, 0x08, + 0xe0, 0x3d, 0x1e, 0x63, + 0xff, 0x65, 0xcc, 0x08, + 0xff, 0x12, 0xda, 0x0c, + 0xff, 0x06, 0xd4, 0x0c, + 0xff, 0x65, 0x0c, 0x08, + 0x02, 0x0b, 0x1a, 0x7b, + 0xff, 0x6a, 0xd4, 0x0c, + 0xd1, 0x6a, 0x22, 0x01, + 0x00, 0x65, 0x18, 0x41, + 0xff, 0x65, 0x26, 0x09, + 0x01, 0x0b, 0x32, 0x6b, + 0x10, 0x0c, 0x24, 0x7b, + 0x04, 0x0b, 0x2c, 0x6b, + 0xff, 0x6a, 0xca, 0x08, + 0x04, 0x93, 0x30, 0x6b, + 0x01, 0x94, 0x2e, 0x7b, + 0x10, 0x94, 0x30, 0x6b, + 0xc7, 0x93, 0x26, 0x09, + 0xff, 0x99, 0xd4, 0x08, + 0x08, 0x93, 0x34, 0x6b, + 0xff, 0x6a, 0xd4, 0x0c, + 0x80, 0x36, 0x38, 0x6b, + 0x21, 0x6a, 0x22, 0x05, + 0xff, 0x65, 0x20, 0x09, + 0xff, 0x51, 0x46, 0x63, + 0xff, 0x37, 0xc8, 0x08, + 0xa1, 0x6a, 0x50, 0x43, + 0xff, 0x51, 0xc8, 0x08, + 0xb9, 0x6a, 0x50, 0x43, + 0xff, 0xba, 0x54, 0x73, + 0xff, 0xba, 0x20, 0x09, + 0xff, 0x65, 0xca, 0x18, + 0x00, 0x6c, 0x4a, 0x63, + 0xff, 0x90, 0xca, 0x0c, + 0xff, 0x6a, 0xca, 0x04, + 0x20, 0x36, 0x72, 0x7b, + 0x00, 0x90, 0x3e, 0x5b, + 0xff, 0x65, 0x72, 0x73, + 0xff, 0xba, 0x66, 0x73, + 0xff, 0xbb, 0xcc, 0x08, + 0xff, 0xba, 0x20, 0x09, + 0xff, 0x66, 0x76, 0x09, + 0xff, 0x65, 0x20, 0x09, + 0xff, 0xbb, 0x70, 0x73, + 0xff, 0xba, 0xcc, 0x08, + 0xff, 0xbb, 0x20, 0x09, + 0xff, 0x66, 0x74, 0x09, + 0xff, 0x65, 0x20, 0x0d, + 0xff, 0xba, 0x7e, 0x0c, + 0x00, 0x6a, 0x72, 0x5c, + 0x0d, 0x6a, 0x6a, 0x00, + 0x00, 0x51, 0xfe, 0x43, + 0xff, 0x3f, 0xcc, 0x73, + 0xff, 0x6a, 0xa2, 0x00, + 0x00, 0x3f, 0x3e, 0x5b, + 0xff, 0x65, 0xcc, 0x73, + 0x20, 0x36, 0x6c, 0x00, + 0x20, 0xa0, 0x86, 0x6b, + 0xff, 0xb9, 0xa2, 0x0c, + 0xff, 0x6a, 0xa2, 0x04, + 0xff, 0x65, 0xa4, 0x08, + 0xe0, 0x6a, 0xcc, 0x00, + 0x45, 0x6a, 0xf2, 0x5b, + 0x01, 0x6a, 0xd0, 0x01, + 0x09, 0x6a, 0xd6, 0x01, + 0x80, 0xeb, 0x92, 0x7b, + 0x01, 0x6a, 0xd6, 0x01, + 0x01, 0xe9, 0xa4, 0x34, + 0x88, 0x6a, 0xcc, 0x00, + 0x45, 0x6a, 0xf2, 0x5b, + 0x01, 0x6a, 0x18, 0x01, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x0d, 0x6a, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x5c, + 0xff, 0x99, 0xa4, 0x0c, + 0xff, 0x65, 0xa4, 0x08, + 0xe0, 0x6a, 0xcc, 0x00, + 0x45, 0x6a, 0xf2, 0x5b, + 0x01, 0x6a, 0xd0, 0x01, + 0x01, 0x6a, 0xdc, 0x05, + 0x88, 0x6a, 0xcc, 0x00, + 0x45, 0x6a, 0xf2, 0x5b, + 0x01, 0x6a, 0x18, 0x01, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0x01, 0x6a, 0x26, 0x05, + 0x01, 0x65, 0xd8, 0x31, + 0x09, 0xee, 0xdc, 0x01, + 0x80, 0xee, 0xc2, 0x7b, + 0xff, 0x6a, 0xdc, 0x0d, + 0xff, 0x65, 0x32, 0x09, + 0x0a, 0x93, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x44, + 0xff, 0x37, 0xc8, 0x08, + 0x00, 0x6a, 0x88, 0x5b, + 0xff, 0x52, 0xa2, 0x0c, + 0x01, 0x0c, 0xd2, 0x7b, + 0x04, 0x0c, 0xd2, 0x6b, + 0xe0, 0x03, 0x7a, 0x08, + 0xff, 0x3d, 0x06, 0x0c, + 0xff, 0x8c, 0x10, 0x08, + 0xff, 0x8d, 0x12, 0x08, + 0xff, 0x8e, 0x14, 0x0c, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x08, + 0xff, 0x6c, 0xda, 0x0c, + 0x3d, 0x64, 0xa4, 0x28, + 0x55, 0x64, 0xc8, 0x28, + 0x00, 0x6c, 0xda, 0x18, + 0xff, 0x52, 0xc8, 0x08, + 0x00, 0x6c, 0xda, 0x20, + 0xff, 0x6a, 0xc8, 0x08, + 0x00, 0x6c, 0xda, 0x20, + 0x00, 0x6c, 0xda, 0x24, + 0xff, 0x65, 0xc8, 0x08, + 0xe0, 0x6a, 0xcc, 0x00, + 0x41, 0x6a, 0xee, 0x5b, + 0xff, 0x90, 0xe2, 0x09, + 0x20, 0x6a, 0xd0, 0x01, + 0x04, 0x35, 0x10, 0x7c, + 0x1d, 0x6a, 0xdc, 0x01, + 0xdc, 0xee, 0x0c, 0x64, + 0x00, 0x65, 0x1c, 0x44, + 0x01, 0x6a, 0xdc, 0x01, + 0x20, 0xa0, 0xd8, 0x31, + 0x09, 0xee, 0xdc, 0x01, + 0x80, 0xee, 0x16, 0x7c, + 0x19, 0x6a, 0xdc, 0x01, + 0xd8, 0xee, 0x1a, 0x64, + 0xff, 0x6a, 0xdc, 0x09, + 0x18, 0xee, 0x1e, 0x6c, + 0xff, 0x6a, 0xd4, 0x0c, + 0x88, 0x6a, 0xcc, 0x00, + 0x41, 0x6a, 0xee, 0x5b, + 0x20, 0x6a, 0x18, 0x01, + 0xff, 0x6a, 0x1a, 0x09, + 0xff, 0x6a, 0x1c, 0x09, + 0xff, 0x35, 0x26, 0x09, + 0x04, 0x35, 0x48, 0x6c, + 0xa0, 0x6a, 0xca, 0x00, + 0x20, 0x65, 0xc8, 0x18, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0xff, 0x6c, 0x32, 0x09, + 0x00, 0x65, 0x34, 0x64, + 0x0a, 0x93, 0x26, 0x01, + 0x00, 0x65, 0x64, 0x5c, + 0x04, 0x35, 0x38, 0x7b, + 0xa0, 0x6a, 0x54, 0x5c, + 0x00, 0x65, 0x56, 0x5c, + 0x00, 0x65, 0x56, 0x5c, + 0x00, 0x65, 0x56, 0x44, + 0xff, 0x65, 0xcc, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x08, + 0xff, 0x99, 0xda, 0x0c, + 0x08, 0x94, 0x64, 0x7c, + 0xf7, 0x93, 0x26, 0x09, + 0x08, 0x93, 0x68, 0x6c, + 0xff, 0x6a, 0xd4, 0x0c, + 0xff, 0x40, 0x74, 0x09, + 0xff, 0x90, 0x80, 0x08, + 0xff, 0x6a, 0x72, 0x05, + 0xff, 0x40, 0x80, 0x64, + 0xff, 0x3f, 0x78, 0x64, + 0xff, 0x6a, 0xca, 0x04, + 0xff, 0x3f, 0x20, 0x09, + 0x01, 0x6a, 0x6a, 0x00, + 0x00, 0xb9, 0xfe, 0x5b, + 0x00, 0x90, 0x5c, 0x43, + 0xff, 0x40, 0x20, 0x09, + 0xff, 0xba, 0x80, 0x0c, + 0xff, 0x6a, 0x76, 0x01, + 0xff, 0x3f, 0x74, 0x09, + 0xff, 0x90, 0x7e, 0x08, + 0xff, 0xba, 0x38, 0x73, + 0xff, 0xba, 0x20, 0x09, + 0xff, 0x3f, 0x76, 0x09, + 0xff, 0x3f, 0x20, 0x0d, +}; + +static int aic7xxx_patch12_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch12_func(struct aic7xxx_host *p) +{ + return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895); +} + +static int aic7xxx_patch11_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch11_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_WIDE) != 0); +} + +static int aic7xxx_patch10_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch10_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_ULTRA2) == 0); +} + +static int aic7xxx_patch9_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch9_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_ULTRA) != 0); +} + +static int aic7xxx_patch8_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch8_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_ULTRA2) != 0); +} + +static int aic7xxx_patch7_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch7_func(struct aic7xxx_host *p) +{ + return ((p->flags & AHC_PAGESCBS) == 0); +} + +static int aic7xxx_patch6_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch6_func(struct aic7xxx_host *p) +{ + return ((p->flags & AHC_PAGESCBS) != 0); +} + +static int aic7xxx_patch5_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch5_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_QUEUE_REGS) != 0); +} + +static int aic7xxx_patch4_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch4_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_TWIN) != 0); +} + +static int aic7xxx_patch3_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch3_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_QUEUE_REGS) == 0); +} + +static int aic7xxx_patch2_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch2_func(struct aic7xxx_host *p) +{ + return ((p->features & AHC_CMD_CHAN) != 0); +} + +static int aic7xxx_patch1_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch1_func(struct aic7xxx_host *p) +{ + return ((p->flags & AHC_TARGETMODE) != 0); +} + +static int aic7xxx_patch0_func(struct aic7xxx_host *p); + +static int +aic7xxx_patch0_func(struct aic7xxx_host *p) +{ + return (0); +} + +struct sequencer_patch { + int (*patch_func)(struct aic7xxx_host *); + unsigned int begin :10, + skip_instr :10, + skip_patch :12; +} sequencer_patches[] = { + { aic7xxx_patch1_func, 1, 1, 2 }, + { aic7xxx_patch0_func, 2, 1, 1 }, + { aic7xxx_patch2_func, 3, 2, 1 }, + { aic7xxx_patch3_func, 7, 1, 1 }, + { aic7xxx_patch3_func, 8, 1, 1 }, + { aic7xxx_patch4_func, 11, 4, 1 }, + { aic7xxx_patch5_func, 16, 3, 2 }, + { aic7xxx_patch0_func, 19, 4, 1 }, + { aic7xxx_patch6_func, 23, 1, 1 }, + { aic7xxx_patch7_func, 26, 1, 1 }, + { aic7xxx_patch4_func, 34, 4, 1 }, + { aic7xxx_patch8_func, 38, 3, 2 }, + { aic7xxx_patch0_func, 41, 3, 1 }, + { aic7xxx_patch9_func, 47, 7, 1 }, + { aic7xxx_patch4_func, 55, 3, 1 }, + { aic7xxx_patch8_func, 58, 2, 1 }, + { aic7xxx_patch1_func, 63, 60, 1 }, + { aic7xxx_patch8_func, 164, 1, 2 }, + { aic7xxx_patch0_func, 165, 1, 1 }, + { aic7xxx_patch2_func, 169, 1, 1 }, + { aic7xxx_patch2_func, 172, 1, 2 }, + { aic7xxx_patch0_func, 173, 2, 1 }, + { aic7xxx_patch10_func, 175, 1, 1 }, + { aic7xxx_patch8_func, 182, 1, 2 }, + { aic7xxx_patch0_func, 183, 3, 1 }, + { aic7xxx_patch8_func, 187, 1, 2 }, + { aic7xxx_patch0_func, 188, 1, 1 }, + { aic7xxx_patch8_func, 189, 7, 2 }, + { aic7xxx_patch0_func, 196, 1, 1 }, + { aic7xxx_patch2_func, 201, 13, 2 }, + { aic7xxx_patch0_func, 214, 8, 1 }, + { aic7xxx_patch10_func, 222, 1, 1 }, + { aic7xxx_patch8_func, 227, 1, 1 }, + { aic7xxx_patch8_func, 228, 1, 1 }, + { aic7xxx_patch8_func, 233, 1, 1 }, + { aic7xxx_patch8_func, 235, 2, 1 }, + { aic7xxx_patch8_func, 240, 8, 1 }, + { aic7xxx_patch8_func, 249, 1, 1 }, + { aic7xxx_patch2_func, 250, 2, 2 }, + { aic7xxx_patch0_func, 252, 4, 1 }, + { aic7xxx_patch10_func, 256, 2, 2 }, + { aic7xxx_patch0_func, 258, 1, 1 }, + { aic7xxx_patch11_func, 265, 1, 2 }, + { aic7xxx_patch0_func, 266, 1, 1 }, + { aic7xxx_patch5_func, 328, 1, 2 }, + { aic7xxx_patch0_func, 329, 1, 1 }, + { aic7xxx_patch3_func, 332, 1, 1 }, + { aic7xxx_patch11_func, 351, 1, 2 }, + { aic7xxx_patch0_func, 352, 1, 1 }, + { aic7xxx_patch6_func, 356, 1, 1 }, + { aic7xxx_patch7_func, 364, 3, 2 }, + { aic7xxx_patch0_func, 367, 1, 1 }, + { aic7xxx_patch1_func, 396, 3, 1 }, + { aic7xxx_patch10_func, 410, 1, 1 }, + { aic7xxx_patch2_func, 453, 7, 2 }, + { aic7xxx_patch0_func, 460, 8, 1 }, + { aic7xxx_patch2_func, 469, 4, 2 }, + { aic7xxx_patch0_func, 473, 6, 1 }, + { aic7xxx_patch2_func, 479, 4, 2 }, + { aic7xxx_patch0_func, 483, 3, 1 }, + { aic7xxx_patch2_func, 512, 17, 4 }, + { aic7xxx_patch12_func, 520, 4, 2 }, + { aic7xxx_patch0_func, 524, 2, 1 }, + { aic7xxx_patch0_func, 529, 33, 1 }, + { aic7xxx_patch6_func, 566, 2, 1 }, + { aic7xxx_patch6_func, 569, 9, 1 }, + +}; diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h --- v2.0.35/linux/drivers/scsi/aic7xxx_seq.h Sun Nov 15 10:49:44 1998 +++ linux/drivers/scsi/aic7xxx_seq.h Wed Dec 31 16:00:00 1969 @@ -1,524 +0,0 @@ -/* - * DO NOT EDIT - This file is automatically generated. - */ -static unsigned char seqprog[] = { - 0xff, 0x6a, 0x03, 0x02, - 0x32, 0x6a, 0x00, 0x00, - 0x12, 0x6a, 0x00, 0x00, - 0x00, 0x65, 0x92, 0x16, - 0xf7, 0x01, 0x01, 0x02, - 0xff, 0x4e, 0x64, 0x02, - 0xbf, 0x60, 0x60, 0x02, - 0x60, 0x0b, 0x37, 0x1a, - 0x40, 0x00, 0x05, 0x1a, - 0x08, 0x1f, 0x1f, 0x04, - 0x60, 0x0b, 0x37, 0x1a, - 0x40, 0x00, 0x05, 0x1a, - 0x08, 0x1f, 0x1f, 0x04, - 0xff, 0x3e, 0x1d, 0x18, - 0x40, 0x60, 0x60, 0x00, - 0x00, 0x4d, 0x06, 0x1c, - 0x01, 0x4e, 0x4e, 0x06, - 0xbf, 0x60, 0x60, 0x02, - 0x00, 0x6a, 0xd8, 0x17, - 0xff, 0x4e, 0x64, 0x06, - 0x02, 0x6a, 0x93, 0x17, - 0x0d, 0x6a, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0xff, 0x99, 0x65, 0x02, - 0xff, 0x65, 0x90, 0x02, - 0x0d, 0x6a, 0x35, 0x00, - 0x00, 0x65, 0xb3, 0x17, - 0xff, 0x3e, 0xba, 0x02, - 0xff, 0x90, 0x3e, 0x02, - 0xff, 0x3e, 0x90, 0x02, - 0x00, 0x65, 0x20, 0x16, - 0x00, 0x65, 0x05, 0x10, - 0xf7, 0x1f, 0x65, 0x02, - 0x08, 0xa1, 0x64, 0x02, - 0x00, 0x65, 0x65, 0x00, - 0xff, 0x65, 0x1f, 0x02, - 0xf0, 0xa1, 0x64, 0x02, - 0x0f, 0x05, 0x05, 0x02, - 0x00, 0x05, 0x05, 0x00, - 0x5a, 0x6a, 0x00, 0x01, - 0x12, 0x65, 0x64, 0x00, - 0x00, 0x01, 0x01, 0x00, - 0x31, 0x6a, 0x65, 0x00, - 0x80, 0x37, 0x2d, 0x1a, - 0xff, 0x65, 0x65, 0x06, - 0xff, 0x37, 0x6e, 0x02, - 0xff, 0x6e, 0x64, 0x02, - 0x00, 0x6c, 0x31, 0x1e, - 0x20, 0x01, 0x01, 0x00, - 0x4c, 0x37, 0x64, 0x0a, - 0x08, 0x1f, 0x35, 0x1e, - 0x08, 0x37, 0x37, 0x00, - 0x08, 0x64, 0x64, 0x00, - 0x20, 0x64, 0x65, 0x06, - 0xff, 0x6c, 0x04, 0x03, - 0x40, 0x0b, 0x78, 0x1a, - 0x80, 0x0b, 0x71, 0x1e, - 0xa4, 0x6a, 0x03, 0x00, - 0x40, 0x6a, 0x0b, 0x00, - 0x10, 0x03, 0x6f, 0x1e, - 0xff, 0x50, 0x64, 0x02, - 0x49, 0x6a, 0xa9, 0x17, - 0x01, 0x6a, 0x93, 0x00, - 0xff, 0x6a, 0x65, 0x02, - 0x08, 0x01, 0x01, 0x00, - 0x02, 0x0b, 0x41, 0x1e, - 0xf7, 0x01, 0x01, 0x02, - 0xff, 0x06, 0x66, 0x02, - 0xff, 0x66, 0x99, 0x02, - 0x01, 0x65, 0x65, 0x06, - 0x80, 0x66, 0x48, 0x1e, - 0xff, 0x66, 0x51, 0x02, - 0x10, 0x03, 0x40, 0x1a, - 0xfc, 0x65, 0x64, 0x06, - 0x00, 0x65, 0x4c, 0x12, - 0xff, 0x6a, 0x99, 0x00, - 0x01, 0x64, 0x8c, 0x06, - 0x84, 0x6a, 0x03, 0x00, - 0x08, 0x01, 0x01, 0x00, - 0x02, 0x0b, 0x4f, 0x1e, - 0xff, 0x06, 0x64, 0x02, - 0xff, 0x64, 0x99, 0x02, - 0xff, 0x6a, 0x65, 0x02, - 0x5b, 0x64, 0x64, 0x0a, - 0x00, 0x62, 0x62, 0x06, - 0xfc, 0x65, 0x65, 0x06, - 0xff, 0x6a, 0x6a, 0x02, - 0xfa, 0x65, 0x65, 0x06, - 0xff, 0x6a, 0x6a, 0x02, - 0x04, 0x65, 0x65, 0x06, - 0x0b, 0x65, 0x65, 0x06, - 0xff, 0x65, 0x64, 0x02, - 0x00, 0x8c, 0x8c, 0x06, - 0x02, 0x0b, 0x5d, 0x1e, - 0x01, 0x65, 0x60, 0x18, - 0xf7, 0x01, 0x01, 0x02, - 0xff, 0x06, 0x99, 0x02, - 0xff, 0x65, 0x65, 0x06, - 0xff, 0x65, 0x5d, 0x1a, - 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0x40, 0x51, 0x69, 0x1e, - 0xe4, 0x6a, 0x03, 0x00, - 0x08, 0x01, 0x01, 0x00, - 0x04, 0x6a, 0x5c, 0x17, - 0x01, 0x50, 0x50, 0x06, - 0x01, 0x50, 0x6c, 0x98, - 0xff, 0x6a, 0x50, 0x02, - 0xff, 0x6a, 0x9d, 0x00, - 0x02, 0x6a, 0x91, 0x00, - 0x40, 0x51, 0x6f, 0x1a, - 0xff, 0x6a, 0x03, 0x02, - 0x00, 0x65, 0x05, 0x10, - 0x20, 0x6a, 0x0b, 0x00, - 0xf0, 0x19, 0x37, 0x02, - 0x08, 0x6a, 0x0c, 0x00, - 0x08, 0x11, 0x11, 0x00, - 0x08, 0x6a, 0x28, 0x16, - 0x08, 0x6a, 0x34, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x12, 0x6a, 0x00, 0x00, - 0x40, 0x6a, 0x0b, 0x00, - 0xff, 0x3e, 0x90, 0x02, - 0xff, 0xba, 0x3e, 0x02, - 0xff, 0xa1, 0x37, 0x02, - 0x08, 0x6a, 0x0c, 0x00, - 0x08, 0x11, 0x11, 0x00, - 0x08, 0x6a, 0x28, 0x16, - 0x80, 0x6a, 0x34, 0x00, - 0x80, 0x36, 0x36, 0x00, - 0x00, 0x65, 0x9b, 0x17, - 0xff, 0x3d, 0x64, 0x02, - 0xbf, 0x64, 0x9a, 0x1e, - 0x80, 0x64, 0xc9, 0x1c, - 0xa0, 0x64, 0xd4, 0x1c, - 0xc0, 0x64, 0xd1, 0x1c, - 0xe0, 0x64, 0xf5, 0x1c, - 0x01, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0xf7, 0x11, 0x11, 0x02, - 0x00, 0x65, 0x92, 0x16, - 0xff, 0x06, 0x6a, 0x02, - 0xf7, 0x01, 0x01, 0x02, - 0x09, 0x0c, 0x8f, 0x1e, - 0x08, 0x0c, 0x05, 0x1a, - 0x01, 0x6a, 0x91, 0x00, - 0xff, 0x6a, 0x93, 0x02, - 0xff, 0x6a, 0x04, 0x02, - 0xdf, 0x01, 0x01, 0x02, - 0x01, 0x6a, 0x3d, 0x00, - 0x03, 0x36, 0x36, 0x03, - 0x08, 0x6a, 0x66, 0x00, - 0xa9, 0x6a, 0xa6, 0x17, - 0x00, 0x65, 0xa5, 0x10, - 0x79, 0x6a, 0x35, 0x00, - 0x40, 0x3d, 0x9d, 0x1a, - 0x04, 0x35, 0x35, 0x00, - 0x00, 0x65, 0x6c, 0x17, - 0x10, 0x36, 0x97, 0x1a, - 0x88, 0x6a, 0x66, 0x00, - 0xac, 0x6a, 0xa2, 0x17, - 0x00, 0x65, 0x9f, 0x17, - 0xff, 0xa3, 0x38, 0x02, - 0x39, 0x6a, 0x66, 0x00, - 0xa4, 0x6a, 0xa5, 0x17, - 0xff, 0x38, 0xac, 0x1a, - 0x80, 0x02, 0x02, 0x00, - 0xff, 0x6a, 0x8c, 0x00, - 0xff, 0x6a, 0x8d, 0x00, - 0xff, 0x6a, 0x8e, 0x00, - 0x00, 0x65, 0x9f, 0x17, - 0xe7, 0x35, 0x35, 0x02, - 0x01, 0x38, 0xae, 0x18, - 0xbf, 0x35, 0x35, 0x02, - 0x00, 0x35, 0x61, 0x17, - 0x80, 0x02, 0xc6, 0x1a, - 0xff, 0x65, 0xc0, 0x1e, - 0xff, 0x38, 0x38, 0x06, - 0xff, 0x38, 0xc0, 0x1e, - 0xff, 0x6a, 0x64, 0x02, - 0x08, 0x39, 0x39, 0x06, - 0x00, 0x3a, 0x3a, 0x08, - 0x88, 0x6a, 0x66, 0x00, - 0x39, 0x6a, 0xa5, 0x17, - 0x08, 0x6a, 0x8c, 0x00, - 0xff, 0x6a, 0x8d, 0x02, - 0xff, 0x6a, 0x8e, 0x02, - 0x0d, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0x88, 0x6a, 0xc9, 0x17, - 0x00, 0x65, 0x9f, 0x17, - 0x10, 0x0c, 0xa5, 0x1e, - 0xff, 0x08, 0xa9, 0x02, - 0xff, 0x09, 0xaa, 0x02, - 0xff, 0x0a, 0xab, 0x02, - 0xff, 0x38, 0xa8, 0x02, - 0x10, 0x36, 0x36, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x7f, 0x02, 0x02, 0x02, - 0xe1, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x00, 0x65, 0x6c, 0x17, - 0x88, 0x6a, 0x66, 0x00, - 0xb4, 0x6a, 0xa4, 0x17, - 0xff, 0x6a, 0x8d, 0x02, - 0xff, 0x6a, 0x8e, 0x02, - 0x00, 0x65, 0x9f, 0x17, - 0x3d, 0x6a, 0x61, 0x17, - 0x00, 0x65, 0x82, 0x10, - 0x00, 0x65, 0x6c, 0x17, - 0xff, 0x06, 0xa2, 0x02, - 0x00, 0x65, 0x82, 0x10, - 0xff, 0x34, 0x65, 0x02, - 0x80, 0x65, 0xe6, 0x18, - 0x0f, 0xa1, 0x65, 0x02, - 0x07, 0xa1, 0x65, 0x02, - 0x40, 0xa0, 0x64, 0x02, - 0x00, 0x65, 0x65, 0x00, - 0x80, 0x65, 0x65, 0x00, - 0x80, 0xa0, 0xde, 0x1e, - 0xff, 0x65, 0x06, 0x02, - 0x00, 0x65, 0xe7, 0x10, - 0x20, 0xa0, 0xe9, 0x1e, - 0xff, 0x65, 0x06, 0x02, - 0x00, 0x65, 0x9b, 0x17, - 0xa0, 0x3d, 0xef, 0x18, - 0x23, 0xa0, 0x06, 0x02, - 0x00, 0x65, 0x9b, 0x17, - 0xa0, 0x3d, 0xef, 0x18, - 0x00, 0xb9, 0xe9, 0x10, - 0xff, 0x65, 0xe9, 0x18, - 0xa1, 0x6a, 0x91, 0x00, - 0x10, 0x51, 0xef, 0x1c, - 0x40, 0x6a, 0x0c, 0x00, - 0xff, 0x65, 0x06, 0x02, - 0x00, 0x65, 0x9b, 0x17, - 0xa0, 0x3d, 0xef, 0x18, - 0x10, 0x3d, 0x03, 0x00, - 0x00, 0x65, 0xd4, 0x10, - 0x40, 0x6a, 0x0c, 0x00, - 0xff, 0x34, 0x52, 0x02, - 0x80, 0x34, 0xf3, 0x18, - 0x7f, 0xa0, 0xa0, 0x02, - 0x08, 0x6a, 0x34, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x64, 0x6a, 0x59, 0x17, - 0x80, 0x64, 0x2f, 0x1b, - 0x04, 0x64, 0x22, 0x1d, - 0x02, 0x64, 0x25, 0x1d, - 0x00, 0x6a, 0x02, 0x1d, - 0x03, 0x64, 0x2d, 0x1d, - 0x01, 0x64, 0x20, 0x1d, - 0x07, 0x64, 0x50, 0x1d, - 0x08, 0x64, 0x00, 0x1d, - 0x11, 0x6a, 0x91, 0x00, - 0x07, 0x6a, 0x52, 0x17, - 0xff, 0x06, 0x6a, 0x02, - 0x00, 0x65, 0x82, 0x10, - 0xff, 0xa8, 0x04, 0x1b, - 0xff, 0xa2, 0x0f, 0x1f, - 0x01, 0x6a, 0x35, 0x00, - 0x00, 0xb9, 0xb3, 0x17, - 0xff, 0xa2, 0x0f, 0x1f, - 0x71, 0x6a, 0x91, 0x00, - 0x40, 0x51, 0x0f, 0x19, - 0x0d, 0x6a, 0x35, 0x00, - 0x00, 0xb9, 0xb3, 0x17, - 0xff, 0x3e, 0xba, 0x02, - 0xff, 0x90, 0x3e, 0x02, - 0x00, 0x65, 0x20, 0x16, - 0x00, 0x65, 0x8b, 0x10, - 0x20, 0xa0, 0x16, 0x1b, - 0xff, 0x37, 0x64, 0x02, - 0x00, 0x6a, 0x93, 0x17, - 0x01, 0x6a, 0x93, 0x00, - 0xff, 0x6a, 0x99, 0x00, - 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0xff, 0x4f, 0x64, 0x02, - 0x01, 0x6a, 0x93, 0x17, - 0x01, 0x6a, 0x93, 0x00, - 0xff, 0xb9, 0x99, 0x02, - 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0x01, 0x4f, 0x4f, 0x06, - 0x02, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0xd5, 0x17, - 0x00, 0x65, 0x8b, 0x10, - 0x41, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0x04, 0xa0, 0xa0, 0x00, - 0x00, 0x65, 0xe1, 0x17, - 0x00, 0x65, 0x8b, 0x10, - 0x10, 0x36, 0x00, 0x1f, - 0xff, 0x38, 0xa3, 0x02, - 0xa4, 0x6a, 0x66, 0x00, - 0x39, 0x6a, 0xa5, 0x17, - 0xac, 0x6a, 0x66, 0x00, - 0x14, 0x6a, 0xa5, 0x17, - 0xa9, 0x6a, 0xa6, 0x17, - 0x00, 0x65, 0x00, 0x11, - 0xef, 0x36, 0x36, 0x02, - 0x00, 0x65, 0x00, 0x11, - 0x0f, 0x64, 0x64, 0x02, - 0x07, 0x64, 0x64, 0x02, - 0x00, 0x37, 0x37, 0x00, - 0x00, 0x65, 0x8b, 0x17, - 0xff, 0x51, 0x37, 0x1d, - 0x20, 0x36, 0x3f, 0x1f, - 0x00, 0x90, 0x7d, 0x17, - 0x00, 0x65, 0x40, 0x11, - 0xff, 0x06, 0x6a, 0x02, - 0x00, 0x65, 0x9b, 0x17, - 0xe0, 0x3d, 0x4d, 0x19, - 0x20, 0x12, 0x4d, 0x19, - 0x51, 0x6a, 0x54, 0x17, - 0xff, 0x51, 0x90, 0x02, - 0x20, 0xa0, 0x4d, 0x1f, - 0x00, 0x90, 0x7d, 0x17, - 0x00, 0x65, 0x7a, 0x17, - 0xff, 0x37, 0x64, 0x02, - 0x00, 0xa1, 0x49, 0x19, - 0x04, 0xa0, 0x49, 0x1f, - 0xfb, 0xa0, 0xa0, 0x02, - 0x80, 0x36, 0x36, 0x00, - 0x80, 0xa0, 0x00, 0x1f, - 0x7f, 0xa0, 0xa0, 0x02, - 0xff, 0x6a, 0x52, 0x17, - 0x00, 0x65, 0x00, 0x11, - 0x04, 0xa0, 0x4c, 0x1f, - 0x00, 0x65, 0xe1, 0x17, - 0x00, 0x65, 0x4d, 0x11, - 0x00, 0x65, 0xd5, 0x17, - 0x31, 0x6a, 0x91, 0x00, - 0x0c, 0x6a, 0x52, 0x17, - 0x00, 0x65, 0x00, 0x11, - 0x61, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x00, 0x11, - 0x10, 0x3d, 0x03, 0x00, - 0xff, 0x65, 0x34, 0x03, - 0xff, 0x06, 0x6a, 0x02, - 0x01, 0x0c, 0x55, 0x1f, - 0x04, 0x0c, 0x55, 0x1b, - 0xe0, 0x03, 0x3d, 0x02, - 0xe0, 0x3d, 0x5f, 0x19, - 0xff, 0x65, 0x66, 0x02, - 0xff, 0x12, 0x6d, 0x03, - 0xff, 0x06, 0x6a, 0x03, - 0xff, 0x65, 0x06, 0x02, - 0x02, 0x0b, 0x5d, 0x1f, - 0xff, 0x6a, 0x6a, 0x03, - 0xd1, 0x6a, 0x91, 0x00, - 0x00, 0x65, 0x82, 0x10, - 0xff, 0x65, 0x93, 0x02, - 0x01, 0x0b, 0x69, 0x1b, - 0x10, 0x0c, 0x62, 0x1f, - 0x04, 0x0b, 0x66, 0x1b, - 0xff, 0x6a, 0x65, 0x02, - 0x04, 0x93, 0x68, 0x1b, - 0x01, 0x94, 0x67, 0x1f, - 0x10, 0x94, 0x68, 0x1b, - 0xc7, 0x93, 0x93, 0x02, - 0x38, 0x93, 0x6a, 0x1b, - 0xff, 0x6a, 0x6a, 0x03, - 0x80, 0x36, 0x6b, 0x1b, - 0x21, 0x6a, 0x91, 0x01, - 0xff, 0x65, 0x90, 0x02, - 0xff, 0x51, 0x72, 0x19, - 0xff, 0x37, 0x64, 0x02, - 0xa1, 0x6a, 0x77, 0x11, - 0xff, 0x51, 0x64, 0x02, - 0xb9, 0x6a, 0x77, 0x11, - 0xff, 0xba, 0x79, 0x1d, - 0xff, 0xba, 0x90, 0x02, - 0xff, 0x65, 0x65, 0x06, - 0x00, 0x6c, 0x74, 0x19, - 0xff, 0x90, 0x65, 0x03, - 0xff, 0x6a, 0x65, 0x01, - 0x20, 0x36, 0x88, 0x1f, - 0x00, 0x90, 0x6e, 0x17, - 0xff, 0x65, 0x88, 0x1d, - 0xff, 0xba, 0x82, 0x1d, - 0xff, 0xbb, 0x66, 0x02, - 0xff, 0xba, 0x90, 0x02, - 0xff, 0x66, 0xbb, 0x02, - 0xff, 0x65, 0x90, 0x02, - 0xff, 0xbb, 0x87, 0x1d, - 0xff, 0xba, 0x66, 0x02, - 0xff, 0xbb, 0x90, 0x02, - 0xff, 0x66, 0xba, 0x02, - 0xff, 0x65, 0x90, 0x03, - 0xff, 0xba, 0x3f, 0x03, - 0x00, 0x6a, 0xd8, 0x17, - 0x0d, 0x6a, 0x35, 0x00, - 0x00, 0x51, 0xb3, 0x11, - 0xff, 0x3f, 0x96, 0x1d, - 0xff, 0x6a, 0x51, 0x00, - 0x00, 0x3f, 0x6e, 0x17, - 0xff, 0x65, 0x96, 0x1d, - 0x20, 0x36, 0x36, 0x00, - 0x20, 0xa0, 0x92, 0x1b, - 0xff, 0xb9, 0x51, 0x03, - 0xff, 0x6a, 0x51, 0x01, - 0xff, 0x65, 0x66, 0x02, - 0x45, 0x6a, 0xab, 0x17, - 0x01, 0x6a, 0x8c, 0x01, - 0xff, 0x37, 0x64, 0x02, - 0x00, 0x6a, 0x93, 0x17, - 0x0d, 0x6a, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0xff, 0x99, 0x51, 0x03, - 0x01, 0x0c, 0x9b, 0x1f, - 0x04, 0x0c, 0x9b, 0x1b, - 0xe0, 0x03, 0x3d, 0x02, - 0xff, 0x3d, 0x03, 0x03, - 0xff, 0x8c, 0x08, 0x02, - 0xff, 0x8d, 0x09, 0x02, - 0xff, 0x8e, 0x0a, 0x03, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x02, - 0xff, 0x6c, 0x6d, 0x03, - 0x3d, 0x64, 0x66, 0x0a, - 0x55, 0x64, 0x64, 0x0a, - 0x00, 0x6c, 0x88, 0x06, - 0xff, 0x66, 0x64, 0x02, - 0x00, 0x6c, 0x89, 0x08, - 0xff, 0x6a, 0x64, 0x02, - 0x00, 0x6c, 0x8a, 0x08, - 0x00, 0x6c, 0x8b, 0x08, - 0xff, 0x6a, 0x8d, 0x02, - 0xff, 0x6a, 0x8e, 0x03, - 0xff, 0x65, 0x64, 0x02, - 0x41, 0x6a, 0xa9, 0x17, - 0x1c, 0x6a, 0x8c, 0x00, - 0xff, 0x35, 0x93, 0x02, - 0x04, 0x35, 0xc3, 0x1b, - 0xa0, 0x6a, 0x65, 0x00, - 0x1c, 0x65, 0x64, 0x06, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0xff, 0x6c, 0x99, 0x02, - 0x00, 0x65, 0xba, 0x19, - 0x0a, 0x93, 0x93, 0x00, - 0x00, 0x65, 0xd1, 0x17, - 0x04, 0x35, 0x6b, 0x1f, - 0xa0, 0x6a, 0xc9, 0x17, - 0x00, 0x65, 0xca, 0x17, - 0x00, 0x65, 0xca, 0x17, - 0x00, 0x65, 0xca, 0x11, - 0xff, 0x65, 0x66, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x02, - 0xff, 0x99, 0x6d, 0x03, - 0x08, 0x94, 0xd1, 0x1f, - 0xf7, 0x93, 0x93, 0x02, - 0x08, 0x93, 0xd3, 0x1b, - 0xff, 0x6a, 0x6a, 0x03, - 0xff, 0x40, 0xba, 0x02, - 0xff, 0x90, 0x40, 0x02, - 0xff, 0x6a, 0xb9, 0x01, - 0xff, 0x40, 0xdf, 0x19, - 0xff, 0x3f, 0xdb, 0x19, - 0xff, 0x6a, 0x65, 0x01, - 0xff, 0x3f, 0x90, 0x02, - 0x01, 0x6a, 0x35, 0x00, - 0x00, 0xb9, 0xb3, 0x17, - 0x00, 0x90, 0x7d, 0x11, - 0xff, 0x40, 0x90, 0x02, - 0xff, 0xba, 0x40, 0x03, - 0xff, 0x6a, 0xbb, 0x00, - 0xff, 0x3f, 0xba, 0x02, - 0xff, 0x90, 0x3f, 0x02, - 0xff, 0xba, 0x6b, 0x1d, - 0xff, 0xba, 0x90, 0x02, - 0xff, 0x3f, 0xbb, 0x02, - 0xff, 0x3f, 0x90, 0x03, -}; -#define WIDE 0x20 -#define ULTRA 0x10 -#define SCB_PAGING 0x8 -#define TWIN_CHANNEL 0x4 -#define TARGET_MODE 0x2 -struct sequencer_patch { - int options; - int negative; - int begin; - int end; -} sequencer_patches[] = { - { 0x00000002, 0, 0x001, 0x002 }, - { 0x00000002, 1, 0x002, 0x003 }, - { 0x00000004, 0, 0x009, 0x00d }, - { 0x00000008, 0, 0x012, 0x013 }, - { 0x00000008, 1, 0x018, 0x019 }, - { 0x00000004, 0, 0x020, 0x024 }, - { 0x00000010, 0, 0x02a, 0x031 }, - { 0x00000002, 0, 0x038, 0x071 }, - { 0x00000020, 0, 0x0d6, 0x0d7 }, - { 0x00000020, 1, 0x0d7, 0x0d8 }, - { 0x00000020, 0, 0x12f, 0x130 }, - { 0x00000020, 1, 0x130, 0x131 }, - { 0x00000008, 0, 0x134, 0x135 }, - { 0x00000008, 1, 0x13c, 0x13f }, - { 0x00000008, 0, 0x13f, 0x140 }, - { 0x00000002, 0, 0x15c, 0x15f }, - { 0x00000008, 0, 0x1d5, 0x1d7 }, - { 0x00000008, 0, 0x1d8, 0x1e1 }, - { 0x00000000, 0, 0x000, 0x000 } -}; diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/dtc.c linux/drivers/scsi/dtc.c --- v2.0.35/linux/drivers/scsi/dtc.c Mon Jul 13 13:46:35 1998 +++ linux/drivers/scsi/dtc.c Sun Nov 15 10:33:08 1998 @@ -176,7 +176,7 @@ base = NULL; if (overrides[current_override].address) - base = overrides[current_override].address; + base = (unsigned char *)overrides[current_override].address; else for (; !base && (current_base < NO_BASES); ++current_base) { #if (DTCDEBUG & DTCDEBUG_INIT) @@ -184,9 +184,9 @@ #endif for (sig = 0; sig < NO_SIGNATURES; ++sig) if (!bases[current_base].noauto && !memcmp - (bases[current_base].address + signatures[sig].offset, + ((unsigned char *)(bases[current_base].address + signatures[sig].offset), signatures[sig].string, strlen(signatures[sig].string))) { - base = bases[current_base].address; + base = (unsigned char *)bases[current_base].address; #if (DTCDEBUG & DTCDEBUG_INIT) printk("scsi-dtc : detected board.\n"); #endif diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c --- v2.0.35/linux/drivers/scsi/eata.c Mon Jul 13 13:46:35 1998 +++ linux/drivers/scsi/eata.c Sun Nov 15 10:33:08 1998 @@ -1,6 +1,25 @@ /* * eata.c - Low-level driver for EATA/DMA SCSI host adapters. * + * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111 + * + Added command line option (rs:[y|n]) to reverse the scan order + * of PCI boards. The default is rs:y, which reverses the BIOS order + * while registering PCI boards. The default value rs:y generates + * the same order of all previous revisions of this driver. + * Pls. note that "BIOS order" might have been reversed itself + * after the 2.1.9x PCI modifications in the linux kernel. + * The rs value is ignored when the explicit list of addresses + * is used by the "eata=port0,port1,..." command line option. + * + Added command line option (et:[y|n]) to force use of extended + * translation (255 heads, 63 sectors) as disk geometry. + * The default is et:n, which uses the disk geometry returned + * by scsicam_bios_param. The default value et:n is compatible with + * all previous revisions of this driver. + * + * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104 + * Increased busy timeout from 10 msec. to 200 msec. while + * processing interrupts. + * * 16 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102 * Improved abort handling during the eh recovery process. * @@ -9,7 +28,7 @@ * abort and reset routines. * Added command line options (eh:[y|n]) to choose between * new_eh_code and the old scsi code. - * If linux verion >= 2.1.101 the default is eh:y, while the eh + * If linux version >= 2.1.101 the default is eh:y, while the eh * option is ignored for previous releases and the old scsi code * is used. * @@ -189,6 +208,10 @@ * PM3222 - SmartRAID Adapter for EISA (PM3222W is 16-bit wide SCSI) * PM3224 - SmartRAID Adapter for PCI (PM3224W is 16-bit wide SCSI) * + * The above list is just an indication: as a matter of fact all DPT + * boards using the EATA/DMA protocol are supported by this driver, + * since they use exactely the same programming interface. + * * The DPT PM2001 provides only the EATA/PIO interface and hence is not * supported by this driver. * @@ -251,6 +274,10 @@ * * eh:y use new scsi code (linux 2.2 only); * eh:n use old scsi code; + * et:y force use of extended translation (255 heads, 63 sectors); + * et:n use disk geometry detected by scsicam_bios_param; + * rs:y reverse scan order while detecting PCI boards; + * rs:n use BIOS order while detecting PCI boards; * lc:y enables linked commands; * lc:n disables linked commands; * tc:y enables tagged commands; @@ -261,15 +288,16 @@ * tm:3 use only ordered queue tags; * mq:xx set the max queue depth to the value xx (2 <= xx <= 32). * - * The default value is: "eata=lc:n,tc:n,mq:16,tm:0". An example using - * the list of detection probes could be: - * "eata=0x7410,0x230,lc:y,tc:n,mq:4,eh:n". + * The default value is: "eata=lc:n,tc:n,mq:16,tm:0,et:n,rs:n". + * An example using the list of detection probes could be: + * "eata=0x7410,0x230,lc:y,tc:n,mq:4,eh:n,et:n". * * When loading as a module, parameters can be specified as well. * The above example would be (use 1 in place of y and 0 in place of n): * * modprobe eata io_port=0x7410,0x230 linked_comm=1 tagged_comm=0 \ - * max_queue_depth=4 tag_mode=0 use_new_eh_code=0 + * max_queue_depth=4 tag_mode=0 use_new_eh_code=0 \ + * ext_tran=0 rev_scan=1 * * ---------------------------------------------------------------------------- * In this implementation, linked commands are designed to work with any DISK @@ -303,6 +331,19 @@ * When the driver detects a batch including overlapping requests * (a really rare event) strict serial (pid) order is enforced. * ---------------------------------------------------------------------------- + * The extended translation option (et:y) is useful when using large physical + * disks/arrays. It could also be useful when switching between Adaptec boards + * and DPT boards without reformatting the disk. + * When a boot disk is partitioned with extended translation, in order to + * be able to boot it with a DPT board is could be necessary to add to + * lilo.conf additional commands as in the following example: + * + * fix-table + * disk=/dev/sda bios=0x80 sectors=63 heads=128 cylindres=546 + * + * where the above geometry should be replaced with the one reported at + * power up by the DPT controller. + * ---------------------------------------------------------------------------- * * The boards are named EATA0, EATA1,... according to the detection order. * @@ -326,6 +367,8 @@ MODULE_PARM(max_queue_depth, "i"); MODULE_PARM(tag_mode, "i"); MODULE_PARM(use_new_eh_code, "i"); +MODULE_PARM(ext_tran, "i"); +MODULE_PARM(rev_scan, "i"); MODULE_AUTHOR("Dario Ballabio"); #endif @@ -409,6 +452,7 @@ #undef DEBUG_RESET #undef DEBUG_GENERATE_ERRORS #undef DEBUG_GENERATE_ABORTS +#undef DEBUG_GEOMETRY #define MAX_ISA 4 #define MAX_VESA 0 @@ -659,6 +703,8 @@ static int setup_done = FALSE; static int link_statistics = 0; static int tag_mode = TAG_MIXED; +static int ext_tran = FALSE; +static int rev_scan = TRUE; #if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE) static int tagged_comm = TRUE; @@ -1063,9 +1109,9 @@ if (j == 0) { printk("EATA/DMA 2.0x: Copyright (C) 1994-1998 Dario Ballabio.\n"); - printk("%s config options -> tc:%c, lc:%c, mq:%d, eh:%c.\n", - driver_name, tag_type, YESNO(linked_comm), - max_queue_depth, YESNO(use_new_eh_code)); + printk("%s config options -> tc:%c, lc:%c, mq:%d, eh:%c, rs:%c, et:%c.\n", + driver_name, tag_type, YESNO(linked_comm), max_queue_depth, + YESNO(use_new_eh_code), YESNO(rev_scan), YESNO(ext_tran)); } printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d.\n", @@ -1128,6 +1174,8 @@ else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val; else if (!strncmp(cur, "ls:", 3)) link_statistics = val; else if (!strncmp(cur, "eh:", 3)) use_new_eh_code = val; + else if (!strncmp(cur, "et:", 3)) ext_tran = val; + else if (!strncmp(cur, "rs:", 3)) rev_scan = val; if ((cur = strchr(cur, ','))) ++cur; } @@ -1161,8 +1209,8 @@ if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) continue; - /* Reverse the returned address order */ - io_port[MAX_INT_PARAM + MAX_PCI - k] = + /* Order addresses according to rev_scan value */ + io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] = (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0; } @@ -1189,8 +1237,8 @@ if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) continue; - /* Reverse the returned address order */ - io_port[MAX_INT_PARAM + MAX_PCI - k] = + /* Order addresses according to rev_scan value */ + io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] = (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0; } @@ -1842,6 +1890,23 @@ #endif /* new_eh_code */ +int eata2x_biosparam(Disk *disk, kdev_t dev, int *dkinfo) { + int size = disk->capacity; + + if (ext_tran || (scsicam_bios_param(disk, dev, dkinfo) < 0)) { + dkinfo[0] = 255; + dkinfo[1] = 63; + dkinfo[2] = size / (dkinfo[0] * dkinfo[1]); + } + +#if defined (DEBUG_GEOMETRY) + printk ("%s: biosparam, head=%d, sec=%d, cyl=%d.\n", driver_name, + dkinfo[0], dkinfo[1], dkinfo[2]); +#endif + + return FALSE; +} + static void sort(unsigned long sk[], unsigned int da[], unsigned int n, unsigned int rev) { unsigned int i, j, k, y; @@ -2020,7 +2085,7 @@ HD(j)->iocount); /* Check if this board is still busy */ - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { + if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) { reg = inb(sh[j]->io_port + REG_STATUS); printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n", BN(j), irq, reg, HD(j)->iocount); diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h --- v2.0.35/linux/drivers/scsi/eata.h Mon Jul 13 13:46:35 1998 +++ linux/drivers/scsi/eata.h Sun Nov 15 10:33:08 1998 @@ -14,8 +14,9 @@ int eata2x_old_abort(Scsi_Cmnd *); int eata2x_reset(Scsi_Cmnd *); int eata2x_old_reset(Scsi_Cmnd *, unsigned int); +int eata2x_biosparam(Disk *, kdev_t, int *); -#define EATA_VERSION "4.31.00" +#define EATA_VERSION "4.33.00" #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) @@ -32,7 +33,7 @@ eh_device_reset_handler: NULL, \ eh_bus_reset_handler: NULL, \ eh_host_reset_handler: eata2x_reset, \ - bios_param: scsicam_bios_param, \ + bios_param: eata2x_biosparam, \ this_id: 7, \ unchecked_isa_dma: 1, \ use_clustering: ENABLE_CLUSTERING, \ @@ -48,7 +49,7 @@ queuecommand: eata2x_queuecommand, \ abort: eata2x_old_abort, \ reset: eata2x_old_reset, \ - bios_param: scsicam_bios_param, \ + bios_param: eata2x_biosparam, \ this_id: 7, \ unchecked_isa_dma: 1, \ use_clustering: ENABLE_CLUSTERING \ diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c --- v2.0.35/linux/drivers/scsi/eata_dma.c Mon Oct 28 14:30:43 1996 +++ linux/drivers/scsi/eata_dma.c Sun Nov 15 10:33:09 1998 @@ -547,7 +547,7 @@ ccb->DataIn = TRUE; /* Input mode */ } - /* FIXME: This will will have to be changed once the midlevel driver + /* FIXME: This will have to be changed once the midlevel driver * allows different HBA IDs on every channel. */ if (cmd->target == sh->this_id) diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/gdth.c linux/drivers/scsi/gdth.c --- v2.0.35/linux/drivers/scsi/gdth.c Mon Aug 4 17:15:07 1997 +++ linux/drivers/scsi/gdth.c Sun Nov 15 10:33:09 1998 @@ -2,7 +2,7 @@ * GDT ISA/EISA/PCI Disk Array Controller driver for Linux * * * * gdth.c * - * Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner * + * Copyright (C) 1995-98 ICP vortex Computersysteme GmbH, Achim Leubner * * * * * * * @@ -20,9 +20,44 @@ * along with this kernel; if not, write to the Free Software * * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * - * Tested with Linux 1.2.13, ..., 2.0.29 * + * Tested with Linux 1.2.13, ..., 2.1.103 * * * * $Log: gdth.c,v $ + * Revision 1.16 1998/09/28 16:08:46 achim + * GDT_PCIMPR: DPMEM remapping, if required + * mdelay() added + * + * Revision 1.15 1998/06/03 14:54:06 achim + * gdth_delay(), gdth_flush() implemented + * Bugfix: gdth_release() changed + * + * Revision 1.14 1998/05/22 10:01:17 achim + * mj: pcibios_strerror() removed + * Improved SMP support (if version >= 2.1.95) + * gdth_halt(): halt_called flag added (if version < 2.1) + * + * Revision 1.13 1998/04/16 09:14:57 achim + * Reserve drives (for raw service) implemented + * New error handling code enabled + * Get controller name from board_info() IOCTL + * Final round of PCI device driver patches by Martin Mares + * + * Revision 1.12 1998/03/03 09:32:37 achim + * Fibre channel controller support added + * + * Revision 1.11 1998/01/27 16:19:14 achim + * SA_SHIRQ added + * add_timer()/del_timer() instead of GDTH_TIMER + * scsi_add_timer()/scsi_del_timer() instead of SCSI_TIMER + * New error handling included + * + * Revision 1.10 1997/10/31 12:29:57 achim + * Read heads/sectors from host drive + * + * Revision 1.9 1997/09/04 10:07:25 achim + * IO-mapping with virt_to_bus(), readb(), writeb(), ... + * register_reboot_notifier() to get a notify on shutdown used + * * Revision 1.8 1997/04/02 12:14:30 achim * Version 1.00 (see gdth.h), tested with kernel 2.0.29 * @@ -53,7 +88,7 @@ * Initial revision * * - * $Id: gdth.c,v 1.8 1997/04/02 12:14:30 achim Exp $ + * $Id: gdth.c,v 1.16 1998/09/28 16:08:46 achim Exp $ ************************************************************************/ #ifdef MODULE @@ -64,7 +99,6 @@ #include #include #include -#include #include #include #include @@ -74,10 +108,18 @@ #include #include #include +#if LINUX_VERSION_CODE >= 0x020100 +#include +#else +#include +#endif #include #include #include +#if LINUX_VERSION_CODE >= 0x02015F +#include +#endif #if LINUX_VERSION_CODE >= 0x010300 #include @@ -90,7 +132,34 @@ #include "gdth.h" -#if LINUX_VERSION_CODE >= 0x010346 +/****************************************************************************/ + +/* LILO params: gdth= + * + * Where: is any of the valid IRQs for EISA controllers (10,11,12,14) + * Sets the IRQ of the GDT3000/3020 EISA controller to this value, + * if the IRQ can not automat. detect (controller BIOS disabled) + * See gdth_init_eisa() + * + * You can use the command line gdth=0 to disable the driver + */ +static unchar irqs[MAXHA] = {0xff}; +static unchar disable_gdth_scan = FALSE; + +/* Reserve drives for raw service: Fill the following structure with the + * appropriate values: Controller number, Channel, Target ID + */ +static gdth_reserve_str reserve_list[] = { + /* { 0, 1, 4 }, Example: Controller 0, Channel B, ID 4 */ + { 0xff, 0xff, 0xff } /* end of list */ +}; + +/****************************************************************************/ + +#if LINUX_VERSION_CODE >= 0x02015F +static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs); +static void do_gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs); +#elif LINUX_VERSION_CODE >= 0x010346 static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs); #else static void gdth_interrupt(int irq,struct pt_regs *regs); @@ -116,9 +185,9 @@ static int gdth_search_eisa(ushort eisa_adr); static int gdth_search_isa(ulong bios_adr); static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr); -static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha,int firsttime); -static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha,int firsttime); -static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha,int firsttime); +static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha); +static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha); +static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha); static void gdth_enable_int(int hanum); static int gdth_get_status(unchar *pIStatus,int irq); @@ -128,28 +197,25 @@ static int gdth_wait(int hanum,int index,ulong time); static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1, ulong p2,ulong p3); -static int gdth_search_drives(int hanum,int firsttime); +static int gdth_search_drives(int hanum); + +static void *gdth_mmap(ulong paddr, ulong size); +static void gdth_munmap(void *addr); static const char *gdth_ctr_name(int hanum); + +static void gdth_flush(int hanum); +#if LINUX_VERSION_CODE >= 0x020100 +static int gdth_halt(struct notifier_block *nb, ulong event, void *buf); +#else +static int halt_called = FALSE; void gdth_halt(void); +#endif #ifdef DEBUG_GDTH static unchar DebugState = DEBUG_GDTH; extern int sys_syslog(int,char*,int); -#define LOGEN sys_syslog(7,NULL,0); -#define WAITSEC(a) {ulong idx; for(idx=0;idxjiffies);} #define GDTOFFSOF(a,b) (size_t)&(((a*)0)->b) #define INDEX_OK(i,t) ((i)hostdata))->cmdext) #define DMADATA(a) (&((gdth_ext_str *)((a)->hostdata))->dmaext) + +#if LINUX_VERSION_CODE < 0x010300 +static void *gdth_mmap(ulong paddr, ulong size) +{ + if (paddr >= high_memory) + return NULL; + else + return (void *)paddr; +} +static void gdth_munmap(void *addr) +{ +} +inline ulong virt_to_phys(volatile void *addr) +{ + return (ulong)addr; +} +inline void *phys_to_virt(ulong addr) +{ + return (void *)addr; +} +#define virt_to_bus virt_to_phys +#define bus_to_virt phys_to_virt +#define readb(addr) (*(volatile unchar *)(addr)) +#define readw(addr) (*(volatile ushort *)(addr)) +#define readl(addr) (*(volatile ulong *)(addr)) +#define writeb(b,addr) (*(volatile unchar *)(addr) = (b)) +#define writew(b,addr) (*(volatile ushort *)(addr) = (b)) +#define writel(b,addr) (*(volatile ulong *)(addr) = (b)) +#define memset_io(a,b,c) memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +#elif LINUX_VERSION_CODE < 0x020100 +static int remapped = FALSE; +static void *gdth_mmap(ulong paddr, ulong size) +{ + if ( paddr >= high_memory) { + remapped = TRUE; + return vremap(paddr, size); + } else { + return (void *)paddr; + } +} +static void gdth_munmap(void *addr) +{ + if (remapped) + vfree(addr); + remapped = FALSE; +} +#else +static void *gdth_mmap(ulong paddr, ulong size) +{ + return ioremap(paddr, size); +} +static void gdth_munmap(void *addr) +{ + return iounmap(addr); +} +#endif + + static unchar gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */ static unchar gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */ static unchar gdth_polling; /* polling if TRUE */ @@ -253,6 +378,7 @@ static int wait_index,wait_hanum; /* gdth_wait() */ static int gdth_ctr_count = 0; /* controller count */ static int gdth_ctr_vcount = 0; /* virt. ctr. count */ +static int gdth_ctr_released = 0; /* gdth_release() */ static struct Scsi_Host *gdth_ctr_tab[MAXHA]; /* controller table */ static struct Scsi_Host *gdth_ctr_vtab[MAXHA*MAXBUS]; /* virt. ctr. table */ static unchar gdth_write_through = FALSE; /* write through */ @@ -289,17 +415,14 @@ DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN }; -/* LILO params: gdth= - * - * Where: is any of the valid IRQs for EISA controllers (10,11,12,14) - * Sets the IRQ of the GDT3000/3020 EISA controller to this value, - * if the IRQ can not automat. detect (controller BIOS disabled) - * See gdth_init_eisa() - * - * You can use the command line gdth=0 to disable the driver - */ -static unchar irqs[MAXHA] = {0xff}; -static unchar disable_gdth_scan = FALSE; +/* __initfunc, __initdata macros */ +#if LINUX_VERSION_CODE >= 0x020126 +#include +#else +#define __initfunc(A) A +#define __initdata +#define __init +#endif /* /proc support */ #if LINUX_VERSION_CODE >= 0x010300 @@ -312,10 +435,31 @@ #include "gdth_proc.c" #endif +#if LINUX_VERSION_CODE >= 0x020100 +/* notifier block to get a notify on system shutdown/halt/reboot */ +static struct notifier_block gdth_notifier = { + gdth_halt, NULL, 0 +}; +#endif + +static void gdth_delay(int milliseconds) +{ + if (milliseconds == 0) { + udelay(1); + } else { +#if LINUX_VERSION_CODE >= 0x020168 + mdelay(milliseconds); +#else + int i; + for (i = 0; i < milliseconds; ++i) + udelay(1000); +#endif + } +} /* controller search and initialization functions */ -static int gdth_search_eisa(ushort eisa_adr) +__initfunc (static int gdth_search_eisa(ushort eisa_adr)) { ulong id; @@ -333,19 +477,23 @@ } -static int gdth_search_isa(ulong bios_adr) +__initfunc (static int gdth_search_isa(ulong bios_adr)) { + void *addr; ulong id; TRACE(("gdth_search_isa() bios adr. %lx\n",bios_adr)); - id = *(ulong *)(bios_adr+BIOS_ID_OFFS); - if (id == GDT2_ID) /* GDT2000 */ - return 1; + if ((addr = gdth_mmap(bios_adr+BIOS_ID_OFFS, sizeof(ulong))) != NULL) { + id = readl(addr); + gdth_munmap(addr); + if (id == GDT2_ID) /* GDT2000 */ + return 1; + } return 0; } -static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr) +__initfunc (static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr)) { int error; ulong base0,base1,base2; @@ -353,14 +501,34 @@ TRACE(("gdth_search_pci() device_id %d, index %d\n", device_id,index)); +#if LINUX_VERSION_CODE >= 0x20155 + if (!pci_present()) + return 0; +#else if (!pcibios_present()) return 0; +#endif if (pcibios_find_device(PCI_VENDOR_ID_VORTEX,device_id,index, &pcistr->bus,&pcistr->device_fn)) return 0; /* GDT PCI controller found, now read resources from config space */ +#if LINUX_VERSION_CODE >= 0x20155 + { + struct pci_dev *pdev = pci_find_slot(pcistr->bus, pcistr->device_fn); + base0 = pdev->base_address[0]; + base1 = pdev->base_address[1]; + base2 = pdev->base_address[2]; + if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_ROM_ADDRESS, + (int *) &pcistr->bios))) { + printk("GDT-PCI: error %d reading configuration space", error); + return -1; + } + pcistr->irq = pdev->irq; + } +#else #if LINUX_VERSION_CODE >= 0x010300 #define GDTH_BASEP (int *) #else @@ -380,10 +548,10 @@ GDTH_BASEP&pcistr->bios)) || (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn, PCI_INTERRUPT_LINE,&pcistr->irq))) { - printk("GDT-PCI: error %s reading configuration space", - pcibios_strerror(error)); + printk("GDT-PCI: error %d reading configuration space", error); return -1; } +#endif pcistr->device_id = device_id; if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000 or GDT6000B */ @@ -404,7 +572,7 @@ } -static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha,int firsttime) +__initfunc (static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)) { ulong retries,id; unchar prot_ver,eisacf,i,irq_found; @@ -418,13 +586,13 @@ outb(0xff,eisa_adr+LDOORREG); retries = INIT_RETRIES; - JIFFYWAIT(2); + gdth_delay(20); while (inb(eisa_adr+EDOORREG) != 0xff) { if (--retries == 0) { printk("GDT-EISA: Initialization error (DEINIT failed)\n"); return 0; } - udelay(1000); + gdth_delay(1); TRACE2(("wait for DEINIT: retries=%ld\n",retries)); } prot_ver = inb(eisa_adr+MAILBOXREG); @@ -433,7 +601,7 @@ printk("GDT-EISA: Illegal protocol version\n"); return 0; } - ha->brd = (ulong)eisa_adr; + ha->bmic = eisa_adr; ha->brd_phys = (ulong)eisa_adr >> 12; outl(0,eisa_adr+MAILBOXREG); @@ -448,41 +616,38 @@ outl(1,eisa_adr+MAILBOXREG+8); outb(0xfe,eisa_adr+LDOORREG); retries = INIT_RETRIES; - JIFFYWAIT(2); + gdth_delay(20); while (inb(eisa_adr+EDOORREG) != 0xfe) { if (--retries == 0) { printk("GDT-EISA: Initialization error (get IRQ failed)\n"); return 0; } - udelay(1000); + gdth_delay(1); } - if (firsttime) - ha->irq = inb(eisa_adr+MAILBOXREG); + ha->irq = inb(eisa_adr+MAILBOXREG); outb(0xff,eisa_adr+EDOORREG); TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq)); - if (firsttime) { - /* check the result */ - if (ha->irq == 0) { - TRACE2(("Unknown IRQ, check IRQ table from cmd line !\n")); - for (i=0,irq_found=FALSE; iirq = irqs[i]; - irqs[i] = 0; - printk("GDT-EISA: Can not detect controller IRQ,\n"); - printk("Use IRQ setting from command line (IRQ = %d)\n", - ha->irq); - } else { - printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n"); - printk("the controller BIOS or use command line parameters\n"); - return 0; - } - } - } + /* check the result */ + if (ha->irq == 0) { + TRACE2(("Unknown IRQ, check IRQ table from cmd line !\n")); + for (i=0,irq_found=FALSE; iirq = irqs[i]; + irqs[i] = 0; + printk("GDT-EISA: Can not detect controller IRQ,\n"); + printk("Use IRQ setting from command line (IRQ = %d)\n", + ha->irq); + } else { + printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n"); + printk("the controller BIOS or use command line parameters\n"); + return 0; + } + } } else { eisacf = inb(eisa_adr+EISAREG) & 7; if (eisacf > 4) /* level triggered */ @@ -495,7 +660,7 @@ } -static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha,int firsttime) +__initfunc (static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha)) { register gdt2_dpram_str *dp2_ptr; int i; @@ -504,19 +669,28 @@ TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr)); - ha->brd = bios_adr; + ha->brd = gdth_mmap(bios_adr, sizeof(gdt2_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-ISA: Initialization error (DPMEM remap error)\n"); + return 0; + } dp2_ptr = (gdt2_dpram_str *)ha->brd; - dp2_ptr->io.memlock = 1; /* switch off write protection */ + writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */ /* reset interface area */ - memset((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u)); + memset_io((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u)); + if (readl(&dp2_ptr->u) != 0) { + printk("GDT-PCI: Initialization error (DPMEM write error)\n"); + gdth_munmap(ha->brd); + return 0; + } /* disable board interrupts, read DRQ and IRQ */ - dp2_ptr->io.irqdel = 0xff; - dp2_ptr->io.irqen = 0x00; - dp2_ptr->u.ic.S_Status = 0x00; - dp2_ptr->u.ic.Cmd_Index= 0x00; + writeb(0xff, &dp2_ptr->io.irqdel); + writeb(0x00, &dp2_ptr->io.irqen); + writeb(0x00, &dp2_ptr->u.ic.S_Status); + writeb(0x00, &dp2_ptr->u.ic.Cmd_Index); - irq_drq = dp2_ptr->io.rq; + irq_drq = readb(&dp2_ptr->io.rq); for (i=0; i<3; ++i) { if ((irq_drq & 1)==0) break; @@ -524,7 +698,7 @@ } ha->drq = gdth_drq_tab[i]; - irq_drq = dp2_ptr->io.rq >> 3; + irq_drq = readb(&dp2_ptr->io.rq) >> 3; for (i=1; i<5; ++i) { if ((irq_drq & 1)==0) break; @@ -533,23 +707,25 @@ ha->irq = gdth_irq_tab[i]; /* deinitialize services */ - dp2_ptr->u.ic.S_Info[0] = bios_adr; - dp2_ptr->u.ic.S_Cmd_Indx= 0xff; - dp2_ptr->io.event = 0; + writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]); + writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx); + writeb(0, &dp2_ptr->io.event); retries = INIT_RETRIES; - JIFFYWAIT(2); - while (dp2_ptr->u.ic.S_Status != 0xff) { + gdth_delay(20); + while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) { if (--retries == 0) { printk("GDT-ISA: Initialization error (DEINIT failed)\n"); + gdth_munmap(ha->brd); return 0; } - udelay(1000); + gdth_delay(1); } - prot_ver = (unchar)dp2_ptr->u.ic.S_Info[0]; - dp2_ptr->u.ic.Status = 0; - dp2_ptr->io.irqdel = 0xff; + prot_ver = (unchar)readl(&dp2_ptr->u.ic.S_Info[0]); + writeb(0, &dp2_ptr->u.ic.Status); + writeb(0xff, &dp2_ptr->io.irqdel); if (prot_ver != PROTOCOL_VERSION) { printk("GDT-ISA: Illegal protocol version\n"); + gdth_munmap(ha->brd); return 0; } @@ -559,78 +735,84 @@ ha->brd_phys = bios_adr >> 4; /* special request to controller BIOS */ - dp2_ptr->u.ic.S_Info[0] = 0x00; - dp2_ptr->u.ic.S_Info[1] = 0x00; - dp2_ptr->u.ic.S_Info[2] = 0x01; - dp2_ptr->u.ic.S_Info[3] = 0x00; - dp2_ptr->u.ic.S_Cmd_Indx= 0xfe; - dp2_ptr->io.event = 0; + writel(0x00, &dp2_ptr->u.ic.S_Info[0]); + writel(0x00, &dp2_ptr->u.ic.S_Info[1]); + writel(0x01, &dp2_ptr->u.ic.S_Info[2]); + writel(0x00, &dp2_ptr->u.ic.S_Info[3]); + writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx); + writeb(0, &dp2_ptr->io.event); retries = INIT_RETRIES; - JIFFYWAIT(2); - while (dp2_ptr->u.ic.S_Status != 0xfe) { + gdth_delay(20); + while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) { if (--retries == 0) { printk("GDT-ISA: Initialization error\n"); + gdth_munmap(ha->brd); return 0; } - udelay(1000); + gdth_delay(1); } - dp2_ptr->u.ic.Status = 0; - dp2_ptr->io.irqdel = 0xff; + writeb(0, &dp2_ptr->u.ic.Status); + writeb(0xff, &dp2_ptr->io.irqdel); return 1; } -static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha,int firsttime) +__initfunc (static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)) { register gdt6_dpram_str *dp6_ptr; register gdt6c_dpram_str *dp6c_ptr; register gdt6m_dpram_str *dp6m_ptr; ulong retries; unchar prot_ver; - unchar remapped = FALSE; + int i, found = FALSE; TRACE(("gdth_init_pci()\n")); - if (firsttime) { - ha->brd = pcistr->dpmem; - ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8); - ha->stype = (ulong)pcistr->device_id; - ha->irq = pcistr->irq; - } - + ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8); + ha->stype = (ulong)pcistr->device_id; + ha->irq = pcistr->irq; + if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) { /* GDT6000 or GDT6000B */ - TRACE2(("init_pci() dpmem %lx irq %d\n",ha->brd,ha->irq)); + TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq)); + ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); + return 0; + } dp6_ptr = (gdt6_dpram_str *)ha->brd; /* reset interface area */ - memset((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u)); - if (*(ulong *)&dp6_ptr->u != 0) { + memset_io((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u)); + if (readl(&dp6_ptr->u) != 0) { printk("GDT-PCI: Initialization error (DPMEM write error)\n"); + gdth_munmap(ha->brd); return 0; } /* disable board interrupts, deinit services */ - dp6_ptr->io.irqdel = 0xff; - dp6_ptr->io.irqen = 0x00; - dp6_ptr->u.ic.S_Status = 0x00; - dp6_ptr->u.ic.Cmd_Index= 0x00; - - dp6_ptr->u.ic.S_Info[0] = ha->brd; - dp6_ptr->u.ic.S_Cmd_Indx= 0xff; - dp6_ptr->io.event = 0; + writeb(0xff, &dp6_ptr->io.irqdel); + writeb(0x00, &dp6_ptr->io.irqen);; + writeb(0x00, &dp6_ptr->u.ic.S_Status); + writeb(0x00, &dp6_ptr->u.ic.Cmd_Index); + + writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]); + writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx); + writeb(0, &dp6_ptr->io.event); retries = INIT_RETRIES; - JIFFYWAIT(2); - while (dp6_ptr->u.ic.S_Status != 0xff) { + gdth_delay(20); + while (readb(&dp6_ptr->u.ic.S_Status) != 0xff) { if (--retries == 0) { printk("GDT-PCI: Initialization error (DEINIT failed)\n"); + gdth_munmap(ha->brd); return 0; } - udelay(1000); + gdth_delay(1); } - prot_ver = (unchar)dp6_ptr->u.ic.S_Info[0]; - dp6_ptr->u.ic.S_Status = 0; - dp6_ptr->io.irqdel = 0xff; + prot_ver = (unchar)readl(&dp6_ptr->u.ic.S_Info[0]); + writeb(0, &dp6_ptr->u.ic.S_Status); + writeb(0xff, &dp6_ptr->io.irqdel); if (prot_ver != PROTOCOL_VERSION) { printk("GDT-PCI: Illegal protocol version\n"); + gdth_munmap(ha->brd); return 0; } @@ -638,35 +820,41 @@ ha->ic_all_size = sizeof(dp6_ptr->u); /* special command to controller BIOS */ - dp6_ptr->u.ic.S_Info[0] = 0x00; - dp6_ptr->u.ic.S_Info[1] = 0x00; - dp6_ptr->u.ic.S_Info[2] = 0x01; - dp6_ptr->u.ic.S_Info[3] = 0x00; - dp6_ptr->u.ic.S_Cmd_Indx= 0xfe; - dp6_ptr->io.event = 0; + writel(0x00, &dp6_ptr->u.ic.S_Info[0]); + writel(0x00, &dp6_ptr->u.ic.S_Info[1]); + writel(0x01, &dp6_ptr->u.ic.S_Info[2]); + writel(0x00, &dp6_ptr->u.ic.S_Info[3]); + writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx); + writeb(0, &dp6_ptr->io.event); retries = INIT_RETRIES; - JIFFYWAIT(2); - while (dp6_ptr->u.ic.S_Status != 0xfe) { + gdth_delay(20); + while (readb(&dp6_ptr->u.ic.S_Status) != 0xfe) { if (--retries == 0) { printk("GDT-PCI: Initialization error\n"); + gdth_munmap(ha->brd); return 0; } - udelay(1000); + gdth_delay(1); } - dp6_ptr->u.ic.S_Status = 0; - dp6_ptr->io.irqdel = 0xff; + writeb(0, &dp6_ptr->u.ic.S_Status); + writeb(0xff, &dp6_ptr->io.irqdel); } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, GDT6120, .. */ - if (firsttime) { - ha->plx = (gdt6c_plx_regs *)pcistr->io; - } - TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n", - ha->brd,(ulong)ha->plx,ha->irq)); + ha->plx = (gdt6c_plx_regs *)pcistr->io; + TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n", + pcistr->dpmem,(ulong)ha->plx,ha->irq)); + ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6c_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); + gdth_munmap(ha->brd); + return 0; + } dp6c_ptr = (gdt6c_dpram_str *)ha->brd; /* reset interface area */ - memset((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u)); - if (*(ulong *)&dp6c_ptr->u != 0) { + memset_io((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u)); + if (readl(&dp6c_ptr->u) != 0) { printk("GDT-PCI: Initialization error (DPMEM write error)\n"); + gdth_munmap(ha->brd); return 0; } @@ -674,27 +862,29 @@ outb(0x00,PTR2USHORT(&ha->plx->control1)); outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); - dp6c_ptr->u.ic.S_Status = 0x00; - dp6c_ptr->u.ic.Cmd_Index= 0x00; + writeb(0x00, &dp6c_ptr->u.ic.S_Status); + writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index); - dp6c_ptr->u.ic.S_Info[0] = ha->brd; - dp6c_ptr->u.ic.S_Cmd_Indx= 0xff; + writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]); + writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx); outb(1,PTR2USHORT(&ha->plx->ldoor_reg)); retries = INIT_RETRIES; - JIFFYWAIT(2); - while (dp6c_ptr->u.ic.S_Status != 0xff) { + gdth_delay(20); + while (readb(&dp6c_ptr->u.ic.S_Status) != 0xff) { if (--retries == 0) { printk("GDT-PCI: Initialization error (DEINIT failed)\n"); + gdth_munmap(ha->brd); return 0; } - udelay(1000); + gdth_delay(1); } - prot_ver = (unchar)dp6c_ptr->u.ic.S_Info[0]; - dp6c_ptr->u.ic.Status = 0; + prot_ver = (unchar)readl(&dp6c_ptr->u.ic.S_Info[0]); + writeb(0, &dp6c_ptr->u.ic.Status); if (prot_ver != PROTOCOL_VERSION) { printk("GDT-PCI: Illegal protocol version\n"); + gdth_munmap(ha->brd); return 0; } @@ -702,87 +892,92 @@ ha->ic_all_size = sizeof(dp6c_ptr->u); /* special command to controller BIOS */ - dp6c_ptr->u.ic.S_Info[0] = 0x00; - dp6c_ptr->u.ic.S_Info[1] = 0x00; - dp6c_ptr->u.ic.S_Info[2] = 0x01; - dp6c_ptr->u.ic.S_Info[3] = 0x00; - dp6c_ptr->u.ic.S_Cmd_Indx= 0xfe; + writel(0x00, &dp6c_ptr->u.ic.S_Info[0]); + writel(0x00, &dp6c_ptr->u.ic.S_Info[1]); + writel(0x01, &dp6c_ptr->u.ic.S_Info[2]); + writel(0x00, &dp6c_ptr->u.ic.S_Info[3]); + writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx); outb(1,PTR2USHORT(&ha->plx->ldoor_reg)); retries = INIT_RETRIES; - JIFFYWAIT(2); - while (dp6c_ptr->u.ic.S_Status != 0xfe) { + gdth_delay(20); + while (readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) { if (--retries == 0) { printk("GDT-PCI: Initialization error\n"); + gdth_munmap(ha->brd); return 0; } - udelay(1000); + gdth_delay(1); } - dp6c_ptr->u.ic.S_Status = 0; + writeb(0, &dp6c_ptr->u.ic.S_Status); } else { /* MPR */ - if (ha->brd > 0xfffff) { /* NOT below 1MB */ -#if LINUX_VERSION_CODE >= 0x010300 - /* Linux 1.3.X allow to remap physical pages adresses greater - than the highest physical memory address to kernel virtual - pages using vremap()/vfree(), Linux 1.2.X doesn't */ - TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",ha->brd,ha->irq)); - ha->brd = (ulong)vremap(ha->brd, sizeof(gdt6m_dpram_str)); - if (ha->brd == 0L) { - printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); - return 0; - } - TRACE2(("init_pci_mpr() remapped dpmem %lx\n",ha->brd)); - remapped = TRUE; -#else - printk("GDT-PCI: Initialization error (DPMEM not below 1MB)\n"); - return 0; -#endif - } + TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq)); + ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6m_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); + return 0; + } + /* check and reset interface area */ dp6m_ptr = (gdt6m_dpram_str *)ha->brd; - /* reset interface area */ - memset((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u)); - if (*(ulong *)&dp6m_ptr->u != 0) { - printk("GDT-PCI: Initialization error (DPMEM write error)\n"); -#if LINUX_VERSION_CODE >= 0x010300 - if (remapped) - vfree((void *)ha->brd); -#endif - return 0; - } + writel(DPMEM_MAGIC, &dp6m_ptr->u); + if (readl(&dp6m_ptr->u) != DPMEM_MAGIC) { + printk("GDT-PCI: Cannot access DPMEM at 0x%x (shadowed?)\n", + (int)ha->brd); + found = FALSE; + for (i = 0xC8000; i < 0xE8000; i += 0x4000) { + pcibios_write_config_dword( pcistr->bus, pcistr->device_fn, + PCI_BASE_ADDRESS_0, i ); + gdth_munmap( ha->brd ); + ha->brd = gdth_mmap(i, sizeof(gdt6m_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); + return 0; + } + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + writel(DPMEM_MAGIC, &dp6m_ptr->u); + if (readl(&dp6m_ptr->u) == DPMEM_MAGIC) { + printk("GDT-PCI: Use free address at 0x%x\n", + (int)ha->brd); + found = TRUE; + break; + } + } + if (!found) { + printk("GDT-PCI: No free address found!\n"); + gdth_munmap( ha->brd ); + return 0; + } + } + memset_io((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u)); /* disable board interrupts, deinit services */ - dp6m_ptr->i960r.edoor_en_reg |= 4; - dp6m_ptr->i960r.edoor_reg = 0xff; - dp6m_ptr->u.ic.S_Status = 0x00; - dp6m_ptr->u.ic.Cmd_Index = 0x00; - - dp6m_ptr->u.ic.S_Info[0] = ha->brd; - dp6m_ptr->u.ic.S_Cmd_Indx = 0xff; - dp6m_ptr->i960r.ldoor_reg = 1; + writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) | 4, + &dp6m_ptr->i960r.edoor_en_reg); + writeb(0xff, &dp6m_ptr->i960r.edoor_reg); + writeb(0x00, &dp6m_ptr->u.ic.S_Status); + writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index); + + writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]); + writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx); + writeb(1, &dp6m_ptr->i960r.ldoor_reg); retries = INIT_RETRIES; - JIFFYWAIT(2); - while (dp6m_ptr->u.ic.S_Status != 0xff) { + gdth_delay(20); + while (readb(&dp6m_ptr->u.ic.S_Status) != 0xff) { if (--retries == 0) { printk("GDT-PCI: Initialization error (DEINIT failed)\n"); -#if LINUX_VERSION_CODE >= 0x010300 - if (remapped) - vfree((void *)ha->brd); -#endif + gdth_munmap(ha->brd); return 0; } - udelay(1000); + gdth_delay(1); } - prot_ver = (unchar)dp6m_ptr->u.ic.S_Info[0]; - dp6m_ptr->u.ic.S_Status = 0; + prot_ver = (unchar)readl(&dp6m_ptr->u.ic.S_Info[0]); + writeb(0, &dp6m_ptr->u.ic.S_Status); if (prot_ver != PROTOCOL_VERSION) { printk("GDT-PCI: Illegal protocol version\n"); -#if LINUX_VERSION_CODE >= 0x010300 - if (remapped) - vfree((void *)ha->brd); -#endif + gdth_munmap(ha->brd); return 0; } @@ -790,26 +985,23 @@ ha->ic_all_size = sizeof(dp6m_ptr->u); /* special command to controller BIOS */ - dp6m_ptr->u.ic.S_Info[0] = 0x00; - dp6m_ptr->u.ic.S_Info[1] = 0x00; - dp6m_ptr->u.ic.S_Info[2] = 0x01; - dp6m_ptr->u.ic.S_Info[3] = 0x00; - dp6m_ptr->u.ic.S_Cmd_Indx = 0xfe; - dp6m_ptr->i960r.ldoor_reg = 1; + writel(0x00, &dp6m_ptr->u.ic.S_Info[0]); + writel(0x00, &dp6m_ptr->u.ic.S_Info[1]); + writel(0x01, &dp6m_ptr->u.ic.S_Info[2]); + writel(0x00, &dp6m_ptr->u.ic.S_Info[3]); + writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx); + writeb(1, &dp6m_ptr->i960r.ldoor_reg); retries = INIT_RETRIES; - JIFFYWAIT(2); - while (dp6m_ptr->u.ic.S_Status != 0xfe) { + gdth_delay(20); + while (readb(&dp6m_ptr->u.ic.S_Status) != 0xfe) { if (--retries == 0) { printk("GDT-PCI: Initialization error\n"); -#if LINUX_VERSION_CODE >= 0x010300 - if (remapped) - vfree((void *)ha->brd); -#endif + gdth_munmap(ha->brd); return 0; } - udelay(1000); + gdth_delay(1); } - dp6m_ptr->u.ic.S_Status = 0; + writeb(0, &dp6m_ptr->u.ic.S_Status); } return 1; @@ -818,11 +1010,10 @@ /* controller protocol functions */ -static void gdth_enable_int(int hanum) +__initfunc (static void gdth_enable_int(int hanum)) { gdth_ha_str *ha; ulong flags; - ushort addr; gdt2_dpram_str *dp2_ptr; gdt6_dpram_str *dp6_ptr; gdt6m_dpram_str *dp6m_ptr; @@ -834,27 +1025,27 @@ cli(); if (ha->type == GDT_EISA) { - addr = (ushort)ha->brd; - outb(0xff,addr+EDOORREG); - outb(0xff,addr+EDENABREG); - outb(0x01,addr+EINTENABREG); + outb(0xff, ha->bmic + EDOORREG); + outb(0xff, ha->bmic + EDENABREG); + outb(0x01, ha->bmic + EINTENABREG); } else if (ha->type == GDT_ISA) { dp2_ptr = (gdt2_dpram_str *)ha->brd; - dp2_ptr->io.irqdel = 1; - dp2_ptr->u.ic.Cmd_Index = 0; - dp2_ptr->io.irqen = 1; + writeb(1, &dp2_ptr->io.irqdel); + writeb(0, &dp2_ptr->u.ic.Cmd_Index); + writeb(1, &dp2_ptr->io.irqen); } else if (ha->type == GDT_PCI) { dp6_ptr = (gdt6_dpram_str *)ha->brd; - dp6_ptr->io.irqdel = 1; - dp6_ptr->u.ic.Cmd_Index = 0; - dp6_ptr->io.irqen = 1; + writeb(1, &dp6_ptr->io.irqdel); + writeb(0, &dp6_ptr->u.ic.Cmd_Index); + writeb(1, &dp6_ptr->io.irqen); } else if (ha->type == GDT_PCINEW) { - outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); - outb(0x03,PTR2USHORT(&ha->plx->control1)); + outb(0xff, PTR2USHORT(&ha->plx->edoor_reg)); + outb(0x03, PTR2USHORT(&ha->plx->control1)); } else if (ha->type == GDT_PCIMPR) { dp6m_ptr = (gdt6m_dpram_str *)ha->brd; - dp6m_ptr->i960r.edoor_reg = 0xff; - dp6m_ptr->i960r.edoor_en_reg &= ~4; + writeb(0xff, &dp6m_ptr->i960r.edoor_reg); + writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4, + &dp6m_ptr->i960r.edoor_en_reg); } restore_flags(flags); } @@ -874,15 +1065,15 @@ if (ha->irq != (unchar)irq) /* check IRQ */ continue; if (ha->type == GDT_EISA) - *pIStatus = inb((ushort)ha->brd+EDOORREG); + *pIStatus = inb((ushort)ha->bmic + EDOORREG); else if (ha->type == GDT_ISA) - *pIStatus = ((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index; + *pIStatus = readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index); else if (ha->type == GDT_PCI) - *pIStatus = ((gdt6_dpram_str *)ha->brd)->u.ic.Cmd_Index; + *pIStatus = readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Cmd_Index); else if (ha->type == GDT_PCINEW) *pIStatus = inb(PTR2USHORT(&ha->plx->edoor_reg)); else if (ha->type == GDT_PCIMPR) - *pIStatus = ((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg; + *pIStatus = readb(&((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg); if (*pIStatus) return i; /* board found */ @@ -900,15 +1091,15 @@ ha = HADATA(gdth_ctr_tab[hanum]); if (ha->type == GDT_EISA) - gdtsema0 = (int)inb((ushort)ha->brd+SEMA0REG); + gdtsema0 = (int)inb(ha->bmic + SEMA0REG); else if (ha->type == GDT_ISA) - gdtsema0 = (int)((gdt2_dpram_str *)ha->brd)->u.ic.Sema0; + gdtsema0 = (int)readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Sema0); else if (ha->type == GDT_PCI) - gdtsema0 = (int)((gdt6_dpram_str *)ha->brd)->u.ic.Sema0; + gdtsema0 = (int)readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Sema0); else if (ha->type == GDT_PCINEW) gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg)); else if (ha->type == GDT_PCIMPR) - gdtsema0 = (int)((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg; + gdtsema0 = (int)readb(&((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg); return (gdtsema0 & 1); } @@ -942,15 +1133,15 @@ ha = HADATA(gdth_ctr_tab[hanum]); if (ha->type == GDT_EISA) - outb(1,(ushort)ha->brd+SEMA0REG); + outb(1, ha->bmic + SEMA0REG); else if (ha->type == GDT_ISA) - ((gdt2_dpram_str *)ha->brd)->u.ic.Sema0 = 1; + writeb(1, &((gdt2_dpram_str *)ha->brd)->u.ic.Sema0); else if (ha->type == GDT_PCI) - ((gdt6_dpram_str *)ha->brd)->u.ic.Sema0 = 1; + writeb(1, &((gdt6_dpram_str *)ha->brd)->u.ic.Sema0); else if (ha->type == GDT_PCINEW) - outb(1,PTR2USHORT(&ha->plx->sema0_reg)); + outb(1, PTR2USHORT(&ha->plx->sema0_reg)); else if (ha->type == GDT_PCIMPR) - ((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg = 1; + writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg); } @@ -986,32 +1177,32 @@ /* set offset and service, copy command to DPMEM */ if (ha->type == GDT_ISA) { dp2_ptr = (gdt2_dpram_str *)ha->brd; - dp2_ptr->u.ic.comm_queue[cmd_no].offset = - dp_offset + DPMEM_COMMAND_OFFSET; - dp2_ptr->u.ic.comm_queue[cmd_no].serv_id = - (ushort)cmd_ptr->Service; - memcpy(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + writew(dp_offset + DPMEM_COMMAND_OFFSET, + &dp2_ptr->u.ic.comm_queue[cmd_no].offset); + writew((ushort)cmd_ptr->Service, + &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id); + memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); } else if (ha->type == GDT_PCI) { dp6_ptr = (gdt6_dpram_str *)ha->brd; - dp6_ptr->u.ic.comm_queue[cmd_no].offset = - dp_offset + DPMEM_COMMAND_OFFSET; - dp6_ptr->u.ic.comm_queue[cmd_no].serv_id = - (ushort)cmd_ptr->Service; - memcpy(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + writew(dp_offset + DPMEM_COMMAND_OFFSET, + &dp6_ptr->u.ic.comm_queue[cmd_no].offset); + writew((ushort)cmd_ptr->Service, + &dp6_ptr->u.ic.comm_queue[cmd_no].serv_id); + memcpy_toio(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); } else if (ha->type == GDT_PCINEW) { dp6c_ptr = (gdt6c_dpram_str *)ha->brd; - dp6c_ptr->u.ic.comm_queue[cmd_no].offset = - dp_offset + DPMEM_COMMAND_OFFSET; - dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id = - (ushort)cmd_ptr->Service; - memcpy(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + writew(dp_offset + DPMEM_COMMAND_OFFSET, + &dp6c_ptr->u.ic.comm_queue[cmd_no].offset); + writew((ushort)cmd_ptr->Service, + &dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id); + memcpy_toio(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); } else if (ha->type == GDT_PCIMPR) { dp6m_ptr = (gdt6m_dpram_str *)ha->brd; - dp6m_ptr->u.ic.comm_queue[cmd_no].offset = - dp_offset + DPMEM_COMMAND_OFFSET; - dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id = - (ushort)cmd_ptr->Service; - memcpy(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + writew(dp_offset + DPMEM_COMMAND_OFFSET, + &dp6m_ptr->u.ic.comm_queue[cmd_no].offset); + writew((ushort)cmd_ptr->Service, + &dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id); + memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); } } @@ -1039,17 +1230,17 @@ ha->pccb->Service |= 0x80; if (ha->type == GDT_EISA) { - outb(ha->pccb->Service,(ushort)ha->brd+LDOORREG); + outb(ha->pccb->Service, ha->bmic + LDOORREG); if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */ - outl((ulong)ha->pccb,(ushort)ha->brd+MAILBOXREG); + outl((ulong)ha->pccb, ha->bmic + MAILBOXREG); } else if (ha->type == GDT_ISA) - ((gdt2_dpram_str *)ha->brd)->io.event = 0; + writeb(0, &((gdt2_dpram_str *)ha->brd)->io.event); else if (ha->type == GDT_PCI) - ((gdt6_dpram_str *)ha->brd)->io.event = 0; + writeb(0, &((gdt6_dpram_str *)ha->brd)->io.event); else if (ha->type == GDT_PCINEW) - outb(1,PTR2USHORT(&ha->plx->ldoor_reg)); + outb(1, PTR2USHORT(&ha->plx->ldoor_reg)); else if (ha->type == GDT_PCIMPR) - ((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg = 1; + writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg); } @@ -1075,12 +1266,12 @@ answer_found = TRUE; break; } - udelay(1000); + gdth_delay(1); } while (--time); gdth_from_wait = FALSE; while (gdth_test_busy(hanum)) - udelay(1); + gdth_delay(0); return (answer_found); } @@ -1115,7 +1306,7 @@ cmd_ptr->u.ioctl.subfunc = p1; cmd_ptr->u.ioctl.channel = p2; cmd_ptr->u.ioctl.param_size = (ushort)p3; - cmd_ptr->u.ioctl.p_param = (ulong)ha->pscratch; + cmd_ptr->u.ioctl.p_param = virt_to_bus(ha->pscratch); } else { cmd_ptr->u.cache.DeviceNo = (ushort)p1; cmd_ptr->u.cache.BlockNo = p2; @@ -1131,14 +1322,14 @@ ha->cmd_cnt = 0; gdth_copy_command(hanum); gdth_release_event(hanum); - JIFFYWAIT(2); + gdth_delay(20); if (!gdth_wait(hanum,index,INIT_TIMEOUT)) { printk("GDT: Initialization error (timeout service %d)\n",service); return 0; } if (ha->status != S_BSY || --retries == 0) break; - udelay(1000); + gdth_delay(1); } return (ha->status != S_OK ? 0:1); @@ -1147,7 +1338,7 @@ /* search for devices */ -static int gdth_search_drives(int hanum,int firsttime) +__initfunc (static int gdth_search_drives(int hanum)) { register gdth_ha_str *ha; ushort cdev_cnt,i; @@ -1155,8 +1346,9 @@ ulong drv_cyls, drv_hds, drv_secs; ulong bus_no; gdth_getch_str *chn; + gdth_iochan_str *ioc; - TRACE(("gdth_search_drives() hanum %d flag %d\n",hanum,firsttime)); + TRACE(("gdth_search_drives() hanum %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); /* initialize controller services, at first: screen service */ @@ -1176,57 +1368,77 @@ TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n")); cdev_cnt = (ushort)ha->info; - if (firsttime) { - /* mount all cache devices */ - if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0)) { - printk("GDT: Initialization error cache service (code %d)\n", - ha->status); - return 0; - } - TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n")); + /* mount all cache devices */ + gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0); + TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n")); - /* initialize cache service after mountall */ - if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) { - printk("GDT: Initialization error cache service (code %d)\n", - ha->status); - return 0; - } - TRACE2(("gdth_search_drives() CACHES. init. after mountall\n")); - cdev_cnt = (ushort)ha->info; + /* initialize cache service after mountall */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives() CACHES. init. after mountall\n")); + cdev_cnt = (ushort)ha->info; - /* detect number of SCSI buses */ - chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]); - for (bus_no=0; bus_nochannel_no = bus_no; - if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, - SCSI_CHAN_CNT | L_CTRL_PATTERN, - IO_CHANNEL | INVALID_CHANNEL, - sizeof(gdth_getch_str))) { - if (bus_no == 0) { - printk("GDT: Error detecting SCSI channel count (0x%x)\n", - ha->status); - return 0; - } - break; - } - if (chn->siop_id < MAXID) - ha->id[bus_no][chn->siop_id].type = SIOP_DTYP; - } - ha->bus_cnt = (unchar)bus_no; - TRACE2(("gdth_search_drives() %d SCSI channels\n",ha->bus_cnt)); - - /* read cache configuration */ - if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO, - INVALID_CHANNEL,sizeof(gdth_cinfo_str))) { - printk("GDT: Initialization error cache service (code %d)\n", - ha->status); - return 0; - } - ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar; - TRACE2(("gdth_search_drives() cinfo: vs %lx sta %d str %d dw %d b %d\n", - ha->cpar.version,ha->cpar.state,ha->cpar.strategy, - ha->cpar.write_back,ha->cpar.block_size)); + /* detect number of SCSI buses - try new IOCTL */ + ioc = (gdth_iochan_str *)DMADATA(gdth_ctr_tab[hanum]); + ioc->version = -1UL; + ioc->list_entries = MAXBUS; + ioc->first_chan = 0; + ioc->last_chan = MAXBUS-1; + ioc->list_offset = GDTOFFSOF(gdth_iochan_str, list[0]); + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,GET_IOCHAN_DESC, + INVALID_CHANNEL,sizeof(gdth_iochan_str))) { + TRACE2(("GET_IOCHAN_DESC supported!\n")); + ha->bus_cnt = ioc->chan_count; + for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no) + if (ioc->list[bus_no].proc_id < MAXID) + ha->id[bus_no][ioc->list[bus_no].proc_id].type = SIOP_DTYP; + } else { + /* old method */ + chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]); + for (bus_no = 0; bus_no < MAXBUS; ++bus_no) { + chn->channel_no = bus_no; + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, + SCSI_CHAN_CNT | L_CTRL_PATTERN, + IO_CHANNEL | INVALID_CHANNEL, + sizeof(gdth_getch_str))) { + if (bus_no == 0) { + printk("GDT: Error detecting SCSI channel count (0x%x)\n", + ha->status); + return 0; + } + break; + } + if (chn->siop_id < MAXID) + ha->id[bus_no][chn->siop_id].type = SIOP_DTYP; + } + ha->bus_cnt = (unchar)bus_no; + } + TRACE2(("gdth_search_drives() %d SCSI channels\n",ha->bus_cnt)); + + /* read cache configuration */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO, + INVALID_CHANNEL,sizeof(gdth_cinfo_str))) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar; + TRACE2(("gdth_search_drives() cinfo: vs %lx sta %d str %d dw %d b %d\n", + ha->cpar.version,ha->cpar.state,ha->cpar.strategy, + ha->cpar.write_back,ha->cpar.block_size)); + + /* read board info, fill ctr_name[] */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_INFO, + INVALID_CHANNEL,sizeof(gdth_binfo_str))) { + TRACE2(("BOARD_INFO supported!\n")); + strcpy(ha->ctr_name, ((gdth_binfo_str *)DMADATA(gdth_ctr_tab[hanum]))->type_string); + } else { + strcpy(ha->ctr_name, gdth_ctr_name(hanum)); } + TRACE2(("Controller name: %s\n",ha->ctr_name)); /* initialize raw service */ if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) { @@ -1241,8 +1453,7 @@ if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER, 0,0)) { TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n")); - if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0)) - { + if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0)) { TRACE2(("gdth_search_dr(): get feat RAWSERVICE %ld\n", ha->info)); ha->raw_feat = (ushort)ha->info; @@ -1260,9 +1471,20 @@ } } - /* if it is not the first scan, we are ready */ - if (!firsttime) - return 1; + /* reserve drives for raw service */ + for (i = 0; reserve_list[i].hanum != 0xff; ++i) { + if (reserve_list[i].hanum < MAXHA && reserve_list[i].hanum == hanum && + reserve_list[i].bus < MAXBUS && reserve_list[i].id < MAXID) { + TRACE2(("gdth_search_drives(): reserve ha %d bus %d id %d\n", + reserve_list[i].hanum, reserve_list[i].bus, + reserve_list[i].id)); + if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_RESERVE,0, + reserve_list[i].bus, reserve_list[i].id)) { + printk("GDT: Error raw service (RESERVE, code %d)\n", + ha->status); + } + } + } /* scanning for raw devices */ for (b=0; bbus_cnt; ++b) { @@ -1301,27 +1523,33 @@ ha->id[b][t].hostdrive = i; /* evaluate mapping (sectors per head, heads per cylinder) */ - ha->id[b][t].size &= ~SECS32; - drv_cyls = ha->id[b][t].size /HEADS/SECS; - if (drv_cyls <= MAXCYLS) { - drv_hds = HEADS; - drv_secs= SECS; - } else { /* too high for 64*32 */ - drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS; - if (drv_cyls <= MAXCYLS) { - drv_hds = MEDHEADS; - drv_secs= MEDSECS; - } else { /* too high for 127*63 */ - drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS; - drv_hds = BIGHEADS; - drv_secs= BIGSECS; - } - } + ha->id[b][t].size &= ~SECS32; + if (ha->info2 == 0) { + drv_cyls = ha->id[b][t].size /HEADS/SECS; + if (drv_cyls <= MAXCYLS) { + drv_hds = HEADS; + drv_secs= SECS; + } else { /* too high for 64*32 */ + drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS; + if (drv_cyls <= MAXCYLS) { + drv_hds = MEDHEADS; + drv_secs= MEDSECS; + } else { /* too high for 127*63 */ + drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS; + drv_hds = BIGHEADS; + drv_secs= BIGSECS; + } + } + } else { + drv_hds = ha->info2 & 0xff; + drv_secs = (ha->info2 >> 8) & 0xff; + drv_cyls = ha->id[b][t].size /drv_hds/drv_secs; + } ha->id[b][t].heads = (unchar)drv_hds; ha->id[b][t].secs = (unchar)drv_secs; /* round size */ ha->id[b][t].size = drv_cyls * drv_hds * drv_secs; - TRACE(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n", + TRACE2(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n", i,ha->id[b][t].size,drv_hds,drv_secs)); /* get informations about device */ @@ -1355,8 +1583,8 @@ ha = HADATA(gdth_ctr_tab[hanum]); scp->SCp.this_residual = (int)priority; - gdth_update_timeout(scp, scp->timeout * 6); -#if LINUX_VERSION_CODE >= 0x010400 + gdth_update_timeout(hanum, scp, scp->timeout_per_command * 6); +#if LINUX_VERSION_CODE >= 0x020000 b = scp->channel; #else b = NUMDATA(nscp->host)->busnum; @@ -1365,7 +1593,7 @@ #if LINUX_VERSION_CODE >= 0x010300 if (priority >= DEFAULT_PRI && ha->id[b][t].lock) { TRACE2(("gdth_putq(): locked IO -> update_timeout()\n")); - scp->SCp.buffers_residual = gdth_update_timeout(scp, 0); + scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0); } #endif @@ -1418,7 +1646,7 @@ for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) { if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr) pscp = (Scsi_Cmnd *)pscp->SCp.ptr; -#if LINUX_VERSION_CODE >= 0x010400 +#if LINUX_VERSION_CODE >= 0x020000 b = nscp->channel; #else b = NUMDATA(nscp->host)->busnum; @@ -1434,7 +1662,7 @@ return; } while (gdth_test_busy(hanum)) - udelay(1000); + gdth_delay(1); } firsttime = FALSE; } @@ -1472,7 +1700,10 @@ TRACE2(("Prevent r. nonremov. drive->do nothing\n")); nscp->result = DID_OK << 16; restore_flags( flags ); - nscp->scsi_done(nscp); + if (!nscp->SCp.have_data_in) + nscp->SCp.have_data_in++; + else + nscp->scsi_done(nscp); save_flags( flags ); cli(); } else { @@ -1500,7 +1731,10 @@ nscp->cmnd[0]); nscp->result = DID_ABORT << 16; restore_flags( flags ); - nscp->scsi_done( nscp ); + if (!nscp->SCp.have_data_in) + nscp->SCp.have_data_in++; + else + nscp->scsi_done(nscp); save_flags( flags ); cli(); break; @@ -1638,7 +1872,10 @@ } restore_flags(*flags); - scp->scsi_done(scp); + if (!scp->SCp.have_data_in) + scp->SCp.have_data_in++; + else + scp->scsi_done(scp); save_flags(*flags); cli(); return 1; @@ -1709,7 +1946,7 @@ cmdp->u.cache.DestAddr= -1UL; sl = (struct scatterlist *)scp->request_buffer; for (i=0; iuse_sg; ++i,++sl) { - cmdp->u.cache.sg_lst[i].sg_ptr = (ulong)sl->address; + cmdp->u.cache.sg_lst[i].sg_ptr = virt_to_bus(sl->address); cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length; } cmdp->u.cache.sg_canz = (ulong)i; @@ -1726,11 +1963,11 @@ if (ha->cache_feat & SCATTER_GATHER) { cmdp->u.cache.DestAddr = -1UL; cmdp->u.cache.sg_canz = 1; - cmdp->u.cache.sg_lst[0].sg_ptr = (ulong)scp->request_buffer; + cmdp->u.cache.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer); cmdp->u.cache.sg_lst[0].sg_len = scp->request_bufflen; cmdp->u.cache.sg_lst[1].sg_len = 0; } else { - cmdp->u.cache.DestAddr = (ulong)scp->request_buffer; + cmdp->u.cache.DestAddr = virt_to_bus(scp->request_buffer); cmdp->u.cache.sg_canz= 0; } } @@ -1806,7 +2043,7 @@ cmdp->u.raw.link_p = NULL; cmdp->u.raw.sdlen = scp->request_bufflen; cmdp->u.raw.sense_len = 16; - cmdp->u.raw.sense_data = (ulong)scp->sense_buffer; + cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer); cmdp->u.raw.direction = gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN; memcpy(cmdp->u.raw.cmd,scp->cmnd,12); @@ -1815,7 +2052,7 @@ cmdp->u.raw.sdata = -1UL; sl = (struct scatterlist *)scp->request_buffer; for (i=0; iuse_sg; ++i,++sl) { - cmdp->u.raw.sg_lst[i].sg_ptr = (ulong)sl->address; + cmdp->u.raw.sg_lst[i].sg_ptr = virt_to_bus(sl->address); cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length; } cmdp->u.raw.sg_ranz = (ulong)i; @@ -1832,11 +2069,11 @@ if (ha->raw_feat & SCATTER_GATHER) { cmdp->u.raw.sdata = -1UL; cmdp->u.raw.sg_ranz= 1; - cmdp->u.raw.sg_lst[0].sg_ptr = (ulong)scp->request_buffer; + cmdp->u.raw.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer); cmdp->u.raw.sg_lst[0].sg_len = scp->request_bufflen; cmdp->u.raw.sg_lst[1].sg_len = 0; } else { - cmdp->u.raw.sdata = (ulong)scp->request_buffer; + cmdp->u.raw.sdata = virt_to_bus(scp->request_buffer); cmdp->u.raw.sg_ranz= 0; } } @@ -2047,6 +2284,17 @@ /* SCSI interface functions */ +#if LINUX_VERSION_CODE >= 0x02015F +static void do_gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs) +{ + ulong flags; + + spin_lock_irqsave(&io_request_lock, flags); + gdth_interrupt(irq, dev_id, regs); + spin_unlock_irqrestore(&io_request_lock, flags); +} +#endif + #if LINUX_VERSION_CODE >= 0x010346 static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs) #else @@ -2091,49 +2339,55 @@ if (ha->type == GDT_EISA) { if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = inw((ushort)ha->brd+MAILBOXREG+8); + CmdStatus = inw(ha->bmic + MAILBOXREG+8); TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = inw((ushort)ha->brd+MAILBOXREG+10); - InfoBytes2 = inl((ushort)ha->brd+MAILBOXREG+4); + Service = inw(ha->bmic + MAILBOXREG+10); + InfoBytes2 = inl(ha->bmic + MAILBOXREG+4); } } else /* no error */ CmdStatus = S_OK; - InfoBytes = inl((ushort)ha->brd+MAILBOXREG+12); - outb(0xff,(ushort)ha->brd+EDOORREG); /* acknowledge interrupt */ - outb(0x00,(ushort)ha->brd+SEMA1REG); /* reset status semaphore */ + InfoBytes = inl(ha->bmic + MAILBOXREG+12); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = inl(ha->bmic + MAILBOXREG+4); + outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */ + outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */ } else if (ha->type == GDT_ISA) { dp2_ptr = (gdt2_dpram_str *)ha->brd; if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = dp2_ptr->u.ic.Status; + CmdStatus = readw(&dp2_ptr->u.ic.Status); TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = dp2_ptr->u.ic.Service; - InfoBytes2 = dp2_ptr->u.ic.Info[1]; + Service = readw(&dp2_ptr->u.ic.Service); + InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]); } } else /* no error */ CmdStatus = S_OK; - InfoBytes = dp2_ptr->u.ic.Info[0]; - dp2_ptr->io.irqdel = 0xff; /* acknowledge interrupt */ - dp2_ptr->u.ic.Cmd_Index = 0; /* reset command index */ - dp2_ptr->io.Sema1 = 0; /* reset status semaphore */ + InfoBytes = readl(&dp2_ptr->u.ic.Info[0]); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]); + writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */ + writeb(0, &dp2_ptr->u.ic.Cmd_Index); /* reset command index */ + writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */ } else if (ha->type == GDT_PCI) { dp6_ptr = (gdt6_dpram_str *)ha->brd; if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = dp6_ptr->u.ic.Status; + CmdStatus = readw(&dp6_ptr->u.ic.Status); TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = dp6_ptr->u.ic.Service; - InfoBytes2 = dp6_ptr->u.ic.Info[1]; + Service = readw(&dp6_ptr->u.ic.Service); + InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]); } } else /* no error */ CmdStatus = S_OK; - InfoBytes = dp6_ptr->u.ic.Info[0]; - dp6_ptr->io.irqdel = 0xff; /* acknowledge interrupt */ - dp6_ptr->u.ic.Cmd_Index = 0; /* reset command index */ - dp6_ptr->io.Sema1 = 0; /* reset status semaphore */ + InfoBytes = readl(&dp6_ptr->u.ic.Info[0]); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]); + writeb(0xff, &dp6_ptr->io.irqdel); /* acknowledge interrupt */ + writeb(0, &dp6_ptr->u.ic.Cmd_Index); /* reset command index */ + writeb(0, &dp6_ptr->io.Sema1); /* reset status semaphore */ } else if (ha->type == GDT_PCINEW) { if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; @@ -2147,23 +2401,27 @@ CmdStatus = S_OK; InfoBytes = inl(PTR2USHORT(&ha->plx->info[0])); - outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); - outb(0x00,PTR2USHORT(&ha->plx->sema1_reg)); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1])); + outb(0xff, PTR2USHORT(&ha->plx->edoor_reg)); + outb(0x00, PTR2USHORT(&ha->plx->sema1_reg)); } else if (ha->type == GDT_PCIMPR) { dp6m_ptr = (gdt6m_dpram_str *)ha->brd; if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = dp6m_ptr->i960r.status; + CmdStatus = readw(&dp6m_ptr->i960r.status); TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = dp6m_ptr->i960r.service; - InfoBytes2 = dp6m_ptr->i960r.info[1]; + Service = readw(&dp6m_ptr->i960r.service); + InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]); } } else /* no error */ CmdStatus = S_OK; - InfoBytes = dp6m_ptr->i960r.info[0]; - dp6m_ptr->i960r.edoor_reg = 0xff; - dp6m_ptr->i960r.sema1_reg = 0; + InfoBytes = readl(&dp6m_ptr->i960r.info[0]); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]); + writeb(0xff, &dp6m_ptr->i960r.edoor_reg); + writeb(0, &dp6m_ptr->i960r.sema1_reg); } else { TRACE2(("gdth_interrupt() unknown controller type\n")); return; @@ -2238,7 +2496,7 @@ if (msg->msg_ext && !msg->msg_answer) { while (gdth_test_busy(hanum)) - udelay(1); + gdth_delay(0); cmdp->Service = SCREENSERVICE; cmdp->RequestBuffer = SCREEN_CMND; gdth_get_cmd_index(hanum); @@ -2275,7 +2533,7 @@ } msg->msg_len = i; while (gdth_test_busy(hanum)) - udelay(1); + gdth_delay(0); cmdp->Service = SCREENSERVICE; cmdp->RequestBuffer = SCREEN_CMND; gdth_get_cmd_index(hanum); @@ -2302,7 +2560,7 @@ scp->result = DID_OK << 16; } else if (ha->status == S_BSY) { TRACE2(("Controller busy -> retry !\n")); - gdth_putq(hanum,scp,DEFAULT_PRI); + gdth_putq(hanum,scp,scp->SCp.this_residual); return 1; } else { if (service == CACHESERVICE) { @@ -2318,7 +2576,7 @@ dvr.eu.sync.status = ha->status; dvr.eu.sync.info = ha->info; dvr.eu.sync.hostdrive = -#if LINUX_VERSION_CODE >= 0x010400 +#if LINUX_VERSION_CODE >= 0x020000 ha->id[scp->channel][scp->target].hostdrive; #else ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive; @@ -2329,15 +2587,17 @@ gdth_store_event(ES_SYNC, service, &dvr); } } else { - if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL) { + if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL || ha->info>=0x100) { scp->result = DID_BAD_TARGET << 16; } else { scp->result = (DID_OK << 16) | ha->info; } } } - scp->SCp.have_data_in++; - scp->scsi_done(scp); + if (!scp->SCp.have_data_in) + scp->SCp.have_data_in++; + else + scp->scsi_done(scp); } return 1; @@ -2456,6 +2716,12 @@ "GDT HA %u, CPU temperature critical", /*55*/ "\003\000\002" "GDT HA %u, CPU temperature OK", +/*56*/ "\005\000\002\006\004" + "GDT HA %u, Host drive %lu created", +/*57*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand restarted", +/*58*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand stopped", }; @@ -2479,7 +2745,7 @@ if (service == SCREENSERVICE) { if (ha->status == MSG_REQUEST) { while (gdth_test_busy(hanum)) - udelay(1); + gdth_delay(0); cmdp->Service = SCREENSERVICE; cmdp->RequestBuffer = SCREEN_CMND; cmd_index = gdth_get_cmd_index(hanum); @@ -2546,8 +2812,9 @@ return 1; } + #ifdef GDTH_STATISTICS -void gdth_timeout(void) +void gdth_timeout(ulong data) { ulong flags,i; Scsi_Cmnd *nscp; @@ -2569,13 +2836,14 @@ act_ints, act_ios, act_stats, act_rq)); act_ints = act_ios = 0; - timer_table[GDTH_TIMER].expires = jiffies + 30*HZ; - timer_active |= 1<= 0x010346 +#if LINUX_VERSION_CODE >= 0x02015F + if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#elif LINUX_VERSION_CODE >= 0x010346 if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) #else if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) @@ -2682,13 +2952,15 @@ for (j=0; jid[i][j].type = EMPTY_DTYP; ha->id[i][j].lock = 0; + ha->id[i][j].heads = 0; } } restore_flags(flags); - if (!gdth_search_drives(hanum,TRUE)) { + if (!gdth_search_drives(hanum)) { printk("GDT-ISA: Error during device scan\n"); --gdth_ctr_count; + --gdth_ctr_vcount; save_flags(flags); cli(); #if LINUX_VERSION_CODE >= 0x010346 @@ -2701,9 +2973,9 @@ continue; } -#if LINUX_VERSION_CODE >= 0x010400 +#if LINUX_VERSION_CODE >= 0x020000 shp->max_id = 8; - shp->max_lun = 8; + shp->max_lun = MAXLUN; shp->max_channel = ha->bus_cnt - 1; #else /* register addit. SCSI channels as virtual controllers */ @@ -2733,7 +3005,7 @@ if (gdth_search_eisa(eisa_slot)) { /* controller found */ shp = scsi_register(shtp,sizeof(gdth_ext_str)); ha = HADATA(shp); - if (!gdth_init_eisa(eisa_slot,ha,TRUE)) { + if (!gdth_init_eisa(eisa_slot,ha)) { scsi_unregister(shp); continue; } @@ -2743,7 +3015,9 @@ save_flags(flags); cli(); -#if LINUX_VERSION_CODE >= 0x010346 +#if LINUX_VERSION_CODE >= 0x02015F + if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#elif LINUX_VERSION_CODE >= 0x010346 if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) #else if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) @@ -2779,13 +3053,15 @@ for (j=0; jid[i][j].type = EMPTY_DTYP; ha->id[i][j].lock = 0; + ha->id[i][j].heads = 0; } } restore_flags(flags); - if (!gdth_search_drives(hanum,TRUE)) { + if (!gdth_search_drives(hanum)) { printk("GDT-EISA: Error during device scan\n"); --gdth_ctr_count; + --gdth_ctr_vcount; save_flags(flags); cli(); #if LINUX_VERSION_CODE >= 0x010346 @@ -2798,9 +3074,9 @@ continue; } -#if LINUX_VERSION_CODE >= 0x010400 +#if LINUX_VERSION_CODE >= 0x020000 shp->max_id = 8; - shp->max_lun = 8; + shp->max_lun = MAXLUN; shp->max_channel = ha->bus_cnt - 1; #else /* register addit. SCSI channels as virtual controllers */ @@ -2829,7 +3105,7 @@ } /* scanning for PCI controllers */ - for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDT6x21RP2; ++device_id) { + for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDTMAXRP; ++device_id) { if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 && device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP) continue; @@ -2838,7 +3114,7 @@ break; /* next device_id */ shp = scsi_register(shtp,sizeof(gdth_ext_str)); ha = HADATA(shp); - if (!gdth_init_pci(&pcistr,ha,TRUE)) { + if (!gdth_init_pci(&pcistr,ha)) { scsi_unregister(shp); continue; } @@ -2848,10 +3124,12 @@ save_flags(flags); cli(); -#if LINUX_VERSION_CODE >= 0x010346 - if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#if LINUX_VERSION_CODE >= 0x02015F + if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth",NULL)) +#elif LINUX_VERSION_CODE >= 0x010346 + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth",NULL)) #else - if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth")) #endif { printk("GDT-PCI: Unable to allocate IRQ\n"); @@ -2882,13 +3160,15 @@ for (j=0; jid[i][j].type = EMPTY_DTYP; ha->id[i][j].lock = 0; + ha->id[i][j].heads = 0; } } restore_flags(flags); - if (!gdth_search_drives(hanum,TRUE)) { + if (!gdth_search_drives(hanum)) { printk("GDT-PCI: Error during device scan\n"); --gdth_ctr_count; + --gdth_ctr_vcount; save_flags(flags); cli(); #if LINUX_VERSION_CODE >= 0x010346 @@ -2901,9 +3181,9 @@ continue; } -#if LINUX_VERSION_CODE >= 0x010400 - shp->max_id = 8; - shp->max_lun = 8; +#if LINUX_VERSION_CODE >= 0x020000 + shp->max_id = MAXID; + shp->max_lun = MAXLUN; shp->max_channel = ha->bus_cnt - 1; #else /* register addit. SCSI channels as virtual controllers */ @@ -2929,14 +3209,19 @@ } TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count)); - + if (gdth_ctr_count > 0) { #ifdef GDTH_STATISTICS - TRACE2(("gdth_detect(): Initializing timer !\n")); - timer_table[GDTH_TIMER].fn = gdth_timeout; - timer_table[GDTH_TIMER].expires = jiffies + HZ; - timer_active |= 1<= 0x020100 + register_reboot_notifier(&gdth_notifier); +#endif + } gdth_polling = FALSE; return gdth_ctr_vcount; } @@ -2948,9 +3233,11 @@ TRACE2(("gdth_release()\n")); - save_flags(flags); - cli(); if (NUMDATA(shp)->busnum == 0) { + gdth_flush(NUMDATA(shp)->hanum); + + save_flags(flags); + cli(); if (shp->irq) { #if LINUX_VERSION_CODE >= 0x010346 free_irq(shp->irq,NULL); @@ -2961,9 +3248,21 @@ if (shp->dma_channel != 0xff) { free_dma(shp->dma_channel); } + restore_flags(flags); + gdth_ctr_released++; + TRACE2(("gdth_release(): HA %d of %d\n", + gdth_ctr_released, gdth_ctr_count)); + + if (gdth_ctr_released == gdth_ctr_count) { +#ifdef GDTH_STATISTICS + del_timer(&gdth_timer); +#endif +#if LINUX_VERSION_CODE >= 0x020100 + unregister_reboot_notifier(&gdth_notifier); +#endif + } } - restore_flags(flags); scsi_unregister(shp); return 0; } @@ -2980,102 +3279,40 @@ if (ha->type == GDT_EISA) { switch (ha->stype) { case GDT3_ID: - return("GDT3000/3020 (EISA)"); + return("GDT3000/3020"); case GDT3A_ID: - return("GDT3000A/3020A/3050A (EISA)"); + return("GDT3000A/3020A/3050A"); case GDT3B_ID: - return("GDT3000B/3010A (EISA)"); + return("GDT3000B/3010A"); } } else if (ha->type == GDT_ISA) { - return("GDT2000/2020 (ISA)"); + return("GDT2000/2020"); } else if (ha->type == GDT_PCI) { switch (ha->stype) { case PCI_DEVICE_ID_VORTEX_GDT60x0: - return("GDT6000/6020/6050 (PCI)"); + return("GDT6000/6020/6050"); case PCI_DEVICE_ID_VORTEX_GDT6000B: - return("GDT6000B/6010 (PCI)"); - } - } else if (ha->type == GDT_PCINEW) { - switch (ha->stype) { - case PCI_DEVICE_ID_VORTEX_GDT6x10: - return("GDT6110/6510 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x20: - return("GDT6120/6520 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6530: - return("GDT6530 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6550: - return("GDT6550 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x17: - return("GDT6117/6517 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x27: - return("GDT6127/6527 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6537: - return("GDT6537 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6557: - return("GDT6557/6557-ECC (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x15: - return("GDT6115/6515 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x25: - return("GDT6125/6525 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6535: - return("GDT6535 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6555: - return("GDT6555/6555-ECC (PCI)"); + return("GDT6000B/6010"); } - } else if (ha->type == GDT_PCIMPR) { - switch (ha->stype) { - case PCI_DEVICE_ID_VORTEX_GDT6x17RP: - return("GDT6117RP/GDT6517RP (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x27RP: - return("GDT6127RP/GDT6527RP (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6537RP: - return("GDT6537RP (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6557RP: - return("GDT6557RP (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x11RP: - return("GDT6111RP/GDT6511RP (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x21RP: - return("GDT6121RP/GDT6521RP (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x17RP1: - return("GDT6117RP1/GDT6517RP1 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x27RP1: - return("GDT6127RP1/GDT6527RP1 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6537RP1: - return("GDT6537RP1 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6557RP1: - return("GDT6557RP1 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x11RP1: - return("GDT6111RP1/GDT6511RP1 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x21RP1: - return("GDT6121RP1/GDT6521RP1 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x17RP2: - return("GDT6117RP2/GDT6517RP2 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x27RP2: - return("GDT6127RP2/GDT6527RP2 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6537RP2: - return("GDT6537RP2 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6557RP2: - return("GDT6557RP2 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x11RP2: - return("GDT6111RP2/GDT6511RP2 (PCI)"); - case PCI_DEVICE_ID_VORTEX_GDT6x21RP2: - return("GDT6121RP2/GDT6521RP2 (PCI)"); - } - } + } + /* new controllers (GDT_PCINEW, GDT_PCIMPR, ..) use board_info IOCTL! */ + return(""); } const char *gdth_info(struct Scsi_Host *shp) { int hanum; - + gdth_ha_str *ha; + TRACE2(("gdth_info()\n")); hanum = NUMDATA(shp)->hanum; + ha = HADATA(gdth_ctr_tab[hanum]); - return (gdth_ctr_name(hanum)); + return ((const char *)ha->ctr_name); } - +/* old error handling */ int gdth_abort(Scsi_Cmnd *scp) { TRACE2(("gdth_abort() reason %d\n",scp->abort_reason)); @@ -3092,6 +3329,32 @@ return SCSI_RESET_PUNT; } +#if LINUX_VERSION_CODE >= 0x02015F +/* new error handling */ +int gdth_eh_abort(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_eh_abort()\n")); + return FAILED; +} + +int gdth_eh_device_reset(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_eh_device_reset()\n")); + return FAILED; +} + +int gdth_eh_bus_reset(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_eh_bus_reset()\n")); + return FAILED; +} + +int gdth_eh_host_reset(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_eh_host_reset()\n")); + return FAILED; +} +#endif #if LINUX_VERSION_CODE >= 0x010300 int gdth_bios_param(Disk *disk,kdev_t dev,int *ip) @@ -3099,23 +3362,38 @@ int gdth_bios_param(Disk *disk,int dev,int *ip) #endif { - TRACE2(("gdth_bios_param()\n")); + unchar b, t; + int hanum; + gdth_ha_str *ha; + int drv_hds, drv_secs; + + hanum = NUMDATA(disk->device->host)->hanum; + b = disk->device->channel; + t = disk->device->id; + TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t)); + ha = HADATA(gdth_ctr_tab[hanum]); + + if (ha->id[b][t].heads == 0) { + /* raw device: evaluate mapping (sectors per head, heads per cylinder) */ + if (disk->capacity /HEADS/SECS <= MAXCYLS) { + drv_hds = HEADS; + drv_secs= SECS; + } else if (disk->capacity /MEDHEADS/MEDSECS <= MAXCYLS) { + drv_hds = MEDHEADS; + drv_secs= MEDSECS; + } else { + drv_hds = BIGHEADS; + drv_secs= BIGSECS; + } + ha->id[b][t].heads = drv_hds; + ha->id[b][t].secs = drv_secs; + TRACE2(("gdth_bios_param(): raw device -> params evaluated\n")); + } + + ip[0] = ha->id[b][t].heads; + ip[1] = ha->id[b][t].secs; + ip[2] = disk->capacity / ip[0] / ip[1]; - ip[2] = disk->capacity / HEADS / SECS; - if (ip[2] <= MAXCYLS) { - ip[0] = HEADS; - ip[1] = SECS; - } else { - ip[2] = disk->capacity / MEDHEADS / MEDSECS; - if (ip[2] <= MAXCYLS) { - ip[0] = MEDHEADS; - ip[1] = MEDSECS; - } else { - ip[2] = disk->capacity / BIGHEADS / BIGSECS; - ip[0] = BIGHEADS; - ip[1] = BIGSECS; - } - } TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n", ip[0],ip[1],ip[2])); return 0; @@ -3149,7 +3427,7 @@ scp->cmnd[0],scp->target,scp->lun)); scp->scsi_done = (void *)done; - scp->SCp.have_data_in = 0; + scp->SCp.have_data_in = 1; hanum = NUMDATA(scp->host)->hanum; #ifdef GDTH_STATISTICS ++act_ios; @@ -3165,22 +3443,84 @@ return 0; } +/* flush routine */ +static void gdth_flush(int hanum) +{ + int i, j; + gdth_ha_str *ha; + Scsi_Cmnd scp; + Scsi_Device sdev; + gdth_cmd_str gdtcmd; + char cmnd[12]; + + TRACE2(("gdth_flush() hanum %d\n",hanum)); + ha = HADATA(gdth_ctr_tab[hanum]); + memset(&sdev,0,sizeof(Scsi_Device)); + memset(&scp, 0,sizeof(Scsi_Cmnd)); + sdev.host = gdth_ctr_tab[hanum]; + sdev.id = sdev.host->this_id; + scp.cmd_len = 12; + scp.host = gdth_ctr_tab[hanum]; + scp.target = sdev.host->this_id; + scp.device = &sdev; + scp.use_sg = 0; + + for (i = 0; i < MAXBUS; ++i) { + for (j = 0; j < MAXID; ++j) { + if (ha->id[i][j].type == CACHE_DTYP) { + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_FLUSH; + gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive; + gdtcmd.u.cache.BlockNo = 1; + gdtcmd.u.cache.sg_canz = 0; + TRACE2(("gdth_flush(): flush ha %d drive %d\n", + hanum, ha->id[i][j].hostdrive)); + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scp.SCp.this_residual = IOCTL_PRI; + scsi_do_cmd(&scp, cmnd, &gdtcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + 30*HZ, 1); + down(&sem); + } + } + } + } +} /* shutdown routine */ -void gdth_halt() +#if LINUX_VERSION_CODE >= 0x020100 +static int gdth_halt(struct notifier_block *nb, ulong event, void *buf) +#else +void gdth_halt(void) +#endif { - int hanum, i, j; - gdth_ha_str *ha; + int hanum; Scsi_Cmnd scp; Scsi_Device sdev; gdth_cmd_str gdtcmd; char cmnd[12]; +#if LINUX_VERSION_CODE >= 0x020100 + TRACE2(("gdth_halt() event %d\n",event)); + if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) + return NOTIFY_DONE; +#else TRACE2(("gdth_halt()\n")); + if (halt_called) { + TRACE2(("already called\n")); + return; + } + halt_called = TRUE; +#endif printk("GDT: Flushing all host drives .. "); - for (hanum = 0; hanum < gdth_ctr_count; ++hanum) { - ha = HADATA(gdth_ctr_tab[hanum]); + gdth_flush(hanum); + + /* controller reset */ memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); sdev.host = gdth_ctr_tab[hanum]; @@ -3191,32 +3531,6 @@ scp.device = &sdev; scp.use_sg = 0; - /* flush */ - for (i = 0; i < MAXBUS; ++i) { - for (j = 0; j < MAXID; ++j) { - if (ha->id[i][j].type == CACHE_DTYP) { - gdtcmd.BoardNode = LOCALBOARD; - gdtcmd.Service = CACHESERVICE; - gdtcmd.OpCode = GDT_FLUSH; - gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive; - gdtcmd.u.cache.BlockNo = 1; - gdtcmd.u.cache.sg_canz = 0; - TRACE2(("gdth_halt(): flush ha %d drive %d\n", - hanum, ha->id[i][j].hostdrive)); - { - struct semaphore sem = MUTEX_LOCKED; - scp.request.rq_status = RQ_SCSI_BUSY; - scp.request.sem = &sem; - scsi_do_cmd(&scp, cmnd, &gdtcmd, - sizeof(gdth_cmd_str), gdth_scsi_done, - 30*HZ, 1); - down(&sem); - } - } - } - } - - /* controller reset */ gdtcmd.BoardNode = LOCALBOARD; gdtcmd.Service = CACHESERVICE; gdtcmd.OpCode = GDT_RESET; @@ -3225,6 +3539,7 @@ struct semaphore sem = MUTEX_LOCKED; scp.request.rq_status = RQ_SCSI_BUSY; scp.request.sem = &sem; + scp.SCp.this_residual = IOCTL_PRI; scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str), gdth_scsi_done, 10*HZ, 1); @@ -3232,11 +3547,19 @@ } } printk("Done.\n"); + +#ifdef GDTH_STATISTICS + del_timer(&gdth_timer); +#endif +#if LINUX_VERSION_CODE >= 0x020100 + unregister_reboot_notifier(&gdth_notifier); + return NOTIFY_OK; +#endif } /* called from init/main.c */ -void gdth_setup(char *str,int *ints) +__initfunc (void gdth_setup(char *str,int *ints)) { static size_t setup_idx = 0; diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/gdth.h linux/drivers/scsi/gdth.h --- v2.0.35/linux/drivers/scsi/gdth.h Wed Oct 15 15:12:17 1997 +++ linux/drivers/scsi/gdth.h Sun Nov 15 10:33:09 1998 @@ -4,13 +4,13 @@ /* * Header file for the GDT ISA/EISA/PCI Disk Array Controller driver for Linux * - * gdth.h Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner + * gdth.h Copyright (C) 1995-98 ICP vortex Computersysteme GmbH, Achim Leubner * See gdth.c for further informations and * below for supported controller types * * * - * $Id: gdth.h,v 1.7 1997/03/20 16:01:59 achim Exp $ + * $Id: gdth.h,v 1.15 1998/09/28 15:50:10 achim Exp $ */ #include @@ -29,9 +29,9 @@ /* defines, macros */ /* driver version */ -#define GDTH_VERSION_STR "1.00" +#define GDTH_VERSION_STR "1.07" #define GDTH_VERSION 1 -#define GDTH_SUBVERSION 0 +#define GDTH_SUBVERSION 7 /* protocol version */ #define PROTOCOL_VERSION 1 @@ -101,15 +101,26 @@ #define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125 /* GDT6121RP2/GDT6521RP2 */ #endif +#ifndef PCI_DEVICE_ID_VORTEX_GDT6519RD +/* GDT_MPR, Fibre Channel */ +#define PCI_DEVICE_ID_VORTEX_GDT6519RD 0x210 /* GDT6519RD */ +#define PCI_DEVICE_ID_VORTEX_GDT6529RD 0x211 /* GDT6529RD */ +#endif + +#ifndef PCI_DEVICE_ID_VORTEX_GDTMAXRP +/* GDT_MPR, last device ID */ +#define PCI_DEVICE_ID_VORTEX_GDTMAXRP 0x2ff +#endif + /* limits */ #define GDTH_SCRATCH 4096 /* 4KB scratch buffer */ #define GDTH_MAXCMDS 124 #define GDTH_MAXC_P_L 16 /* max. cmds per lun */ #define MAXOFFSETS 128 #define MAXHA 8 -#define MAXID 8 +#define MAXID 16 #define MAXLUN 8 -#define MAXBUS 5 +#define MAXBUS 6 #define MAX_HDRIVES 35 /* max. host drive count */ #define MAX_EVENTS 100 /* event buffer count */ #define MAXCYLS 1024 @@ -147,6 +158,7 @@ #define SECTOR_SIZE 0x200 /* always 512 bytes per sector */ /* DPMEM constants */ +#define DPMEM_MAGIC 0xC0FFEE11 #define IC_HEADER_BYTES 48 #define IC_QUEUE_BYTES 4 #define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS @@ -170,9 +182,11 @@ /* IOCTL command defines */ #define SCSI_CHAN_CNT 5 /* subfunctions */ +#define GET_IOCHAN_DESC 0x5e #define L_CTRL_PATTERN 0x20000000L #define CACHE_INFO 4 #define CACHE_CONFIG 5 +#define BOARD_INFO 0x28 #define IO_CHANNEL 0x00020000L /* channels */ #define INVALID_CHANNEL 0x0000ffffL @@ -254,6 +268,21 @@ unchar siop_state; /* SCSI processor state */ } gdth_getch_str; +/* get raw channel count IOCTL (NEW!) */ +typedef struct { + ulong version; /* version of information (-1UL: newest) */ + unchar list_entries; /* list entry count */ + unchar first_chan; /* first channel number */ + unchar last_chan; /* last channel number */ + unchar chan_count; /* (R) channel count */ + ulong list_offset; /* offset of list[0] */ + struct { + unchar proc_id; /* processor id */ + unchar proc_defect; /* defect ? */ + unchar reserved[2]; + } list[MAXBUS]; +} gdth_iochan_str; + /* cache info/config IOCTL */ typedef struct { ulong version; /* firmware version */ @@ -277,6 +306,35 @@ gdth_cstat_str cstat; } gdth_cinfo_str; +/* board info IOCTL */ +typedef struct { + ulong ser_no; /* serial no. */ + unchar oem_id[2]; /* OEM ID */ + ushort ep_flags; /* eprom flags */ + ulong proc_id; /* processor ID */ + ulong memsize; /* memory size (bytes) */ + unchar mem_banks; /* memory banks */ + unchar chan_type; /* channel type */ + unchar chan_count; /* channel count */ + unchar rdongle_pres; /* dongle present? */ + ulong epr_fw_ver; /* (eprom) firmware version */ + ulong upd_fw_ver; /* (update) firmware version */ + ulong upd_revision; /* update revision */ + char type_string[16]; /* controller name */ + char raid_string[16]; /* RAID firmware name */ + unchar update_pres; /* update present? */ + unchar xor_pres; /* XOR engine present? */ + unchar prom_type; /* ROM type (eprom/flash eprom) */ + unchar prom_count; /* number of ROM devices */ + ulong dup_pres; /* duplexing module present? */ + ulong chan_pres; /* number of expansion channels */ + ulong mem_pres; /* memory expansion installed? */ + unchar ft_bus_system; /* fault bus supported? */ + unchar subtype_valid; /* board_subtype valid? */ + unchar board_subtype; /* controller subtype/hardware level */ + unchar ramparity_pres; /* RAM parity check hardware present? */ +} gdth_binfo_str; + /* scatter/gather element */ typedef struct { ulong sg_ptr; /* address */ @@ -550,9 +608,10 @@ unchar bus_cnt; /* SCSI bus count */ unchar type; /* controller class */ ushort raw_feat; /* feat. raw service (s/g,..) */ - ushort cache_feat; /* feat. cache serv. (s/g,..) */ ulong stype; /* controller subtype */ - ulong brd; /* BMIC/DPRAM address */ + ushort cache_feat; /* feat. cache serv. (s/g,..) */ + ushort bmic; /* BMIC address (EISA) */ + void *brd; /* DPRAM address */ ulong brd_phys; /* slot number/BIOS address */ gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */ gdth_cmd_str *pccb; /* address command structure */ @@ -580,6 +639,7 @@ unchar mode; /* information from /proc */ ushort param_size; gdth_cpar_str cpar; /* controller cache par. */ + char ctr_name[16]; /* controller name */ } gdth_ha_str; /* structure for scsi_register(), SCSI bus != 0 */ @@ -648,12 +708,22 @@ } bd; } gdth_modep_data; +/* stack frame */ typedef struct { ulong b[10]; /* 32 bit compiler ! */ } gdth_stackframe; #pragma pack() + +/* data structure for reserve drives */ +typedef struct { + unchar hanum; + unchar bus; + unchar id; +} gdth_reserve_str; + + /* function prototyping */ int gdth_detect(Scsi_Host_Template *); @@ -668,8 +738,38 @@ #endif const char *gdth_info(struct Scsi_Host *); - -#if LINUX_VERSION_CODE >= 0x010300 +#if LINUX_VERSION_CODE >= 0x02015F +int gdth_bios_param(Disk *,kdev_t,int *); +extern struct proc_dir_entry proc_scsi_gdth; +int gdth_proc_info(char *,char **,off_t,int,int,int); +int gdth_eh_abort(Scsi_Cmnd *scp); +int gdth_eh_device_reset(Scsi_Cmnd *scp); +int gdth_eh_bus_reset(Scsi_Cmnd *scp); +int gdth_eh_host_reset(Scsi_Cmnd *scp); +#define GDTH { proc_dir: &proc_scsi_gdth, \ + proc_info: gdth_proc_info, \ + name: "GDT SCSI Disk Array Controller",\ + detect: gdth_detect, \ + release: gdth_release, \ + info: gdth_info, \ + command: gdth_command, \ + queuecommand: gdth_queuecommand, \ + eh_abort_handler: gdth_eh_abort, \ + eh_device_reset_handler: gdth_eh_device_reset, \ + eh_bus_reset_handler: gdth_eh_bus_reset, \ + eh_host_reset_handler: gdth_eh_host_reset, \ + abort: gdth_abort, \ + reset: gdth_reset, \ + bios_param: gdth_bios_param, \ + can_queue: GDTH_MAXCMDS, \ + this_id: -1, \ + sg_tablesize: GDTH_MAXSG, \ + cmd_per_lun: GDTH_MAXC_P_L, \ + present: 0, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 1 /* use new error code */ } +#elif LINUX_VERSION_CODE >= 0x010300 int gdth_bios_param(Disk *,kdev_t,int *); extern struct proc_dir_entry proc_scsi_gdth; int gdth_proc_info(char *,char **,off_t,int,int,int); diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/gdth_proc.c linux/drivers/scsi/gdth_proc.c --- v2.0.35/linux/drivers/scsi/gdth_proc.c Mon Aug 4 17:15:07 1997 +++ linux/drivers/scsi/gdth_proc.c Sun Nov 15 10:33:09 1998 @@ -1,5 +1,5 @@ /* gdth_proc.c - * $Id: gdth_proc.c,v 1.4 1997/02/25 13:33:47 achim Exp $ + * $Id: gdth_proc.c,v 1.10 1998/06/03 14:53:49 achim Exp $ */ #include "gdth_ioctl.h" @@ -10,7 +10,7 @@ int hanum,busnum,i; TRACE2(("gdth_proc_info() length %d ha %d offs %d inout %d\n", - length,hostno,offset,inout)); + length,hostno,(int)offset,inout)); for (i=0; ihost_no == hostno) @@ -113,6 +113,7 @@ struct semaphore sem = MUTEX_LOCKED; scp.request.rq_status = RQ_SCSI_BUSY; scp.request.sem = &sem; + scp.SCp.this_residual = IOCTL_PRI; scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str), gdth_scsi_done, 30*HZ, 1); @@ -170,7 +171,7 @@ gdtcmd.BoardNode = LOCALBOARD; gdtcmd.Service = CACHESERVICE; gdtcmd.OpCode = GDT_IOCTL; - gdtcmd.u.ioctl.p_param = (ulong)pcpar; + gdtcmd.u.ioctl.p_param = virt_to_bus(pcpar); gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str); gdtcmd.u.ioctl.subfunc = CACHE_CONFIG; gdtcmd.u.ioctl.channel = INVALID_CHANNEL; @@ -179,6 +180,7 @@ struct semaphore sem = MUTEX_LOCKED; scp.request.rq_status = RQ_SCSI_BUSY; scp.request.sem = &sem; + scp.SCp.this_residual = IOCTL_PRI; scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str), gdth_scsi_done, 30*HZ, 1); down(&sem); @@ -257,7 +259,7 @@ piord->size = sizeof(gdth_iord_str) + add_size; if (add_size > 0) { memcpy(piord->iu.general.data, piowr->iu.general.data, add_size); - *ppadd = (ulong)piord->iu.general.data; + *ppadd = virt_to_bus(piord->iu.general.data); } /* do IOCTL */ { @@ -291,7 +293,7 @@ piord->size = sizeof(gdth_iord_str); piord->status = S_OK; if (ha->type == GDT_ISA || ha->type == GDT_EISA) { - piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 10); + piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 0x10); } else if (ha->type != GDT_PCIMPR) { piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6); } else { @@ -433,14 +435,14 @@ /* look for buffer ID in length */ if (id > 4) { -#if LINUX_VERSION_CODE >= 0x010400 +#if LINUX_VERSION_CODE >= 0x020000 size = sprintf(buffer+len, "%s SCSI Disk Array Controller\n", - gdth_ctr_name(hanum)); + ha->ctr_name); #else size = sprintf(buffer+len, "%s SCSI Disk Array Controller (SCSI Bus %d)\n", - gdth_ctr_name(hanum),busnum); + ha->ctr_name,busnum); #endif len += size; pos = begin + len; size = sprintf(buffer+len, @@ -479,7 +481,7 @@ if (len > length) len = length; TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n", - len,pos,begin,offset,length,size)); + len,(int)pos,(int)begin,(int)offset,length,size)); return(len); } @@ -542,16 +544,19 @@ for (i = 0; i < GDTH_MAXCMDS; ++i) { scp = gdth_cmd_tab[i][hanum].cmnd; +#if LINUX_VERSION_CODE >= 0x020000 if (!SPECIAL_SCP(scp) && scp->target == (unchar)id && -#if LINUX_VERSION_CODE >= 0x010400 scp->channel == (unchar)busnum) #else + if (!SPECIAL_SCP(scp) && scp->target == (unchar)id && NUMDATA(scp->host)->busnum == (unchar)busnum) #endif { + scp->SCp.have_data_in = 0; restore_flags(flags); while (!scp->SCp.have_data_in) barrier(); + scp->scsi_done(scp); save_flags(flags); cli(); } @@ -570,15 +575,16 @@ ha = HADATA(gdth_ctr_tab[hanum]); for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { +#if LINUX_VERSION_CODE >= 0x020000 if (scp->target == (unchar)id && -#if LINUX_VERSION_CODE >= 0x010400 scp->channel == (unchar)busnum) #else + if (scp->target == (unchar)id && NUMDATA(scp->host)->busnum == (unchar)busnum) #endif { TRACE2(("gdth_stop_timeout(): update_timeout()\n")); - scp->SCp.buffers_residual = gdth_update_timeout(scp, 0); + scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0); } } restore_flags(flags); @@ -595,30 +601,44 @@ ha = HADATA(gdth_ctr_tab[hanum]); for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { +#if LINUX_VERSION_CODE >= 0x020000 if (scp->target == (unchar)id && -#if LINUX_VERSION_CODE >= 0x010400 scp->channel == (unchar)busnum) #else + if (scp->target == (unchar)id && NUMDATA(scp->host)->busnum == (unchar)busnum) #endif { TRACE2(("gdth_start_timeout(): update_timeout()\n")); - gdth_update_timeout(scp, scp->SCp.buffers_residual); + gdth_update_timeout(hanum, scp, scp->SCp.buffers_residual); } } restore_flags(flags); } -static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout) +static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout) { ulong flags; int oldto; save_flags(flags); cli(); + oldto = scp->timeout_per_command; + scp->timeout_per_command = timeout; - oldto = scp->timeout; - scp->timeout = timeout; +#if LINUX_VERSION_CODE >= 0x02014B + if (timeout == 0) { + del_timer(&scp->eh_timeout); + scp->eh_timeout.data = (unsigned long) NULL; + scp->eh_timeout.expires = 0; + } else { + if (scp->eh_timeout.data != (unsigned long) NULL) + del_timer(&scp->eh_timeout); + scp->eh_timeout.data = (unsigned long) scp; + scp->eh_timeout.expires = jiffies + timeout; + add_timer(&scp->eh_timeout); + } +#else if (timeout > 0) { if (timer_table[SCSI_TIMER].expires == 0) { timer_table[SCSI_TIMER].expires = jiffies + timeout; @@ -628,6 +648,7 @@ timer_table[SCSI_TIMER].expires = jiffies + timeout; } } +#endif restore_flags(flags); return oldto; diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/gdth_proc.h linux/drivers/scsi/gdth_proc.h --- v2.0.35/linux/drivers/scsi/gdth_proc.h Mon Aug 4 17:15:07 1997 +++ linux/drivers/scsi/gdth_proc.h Sun Nov 15 10:33:09 1998 @@ -2,7 +2,7 @@ #define _GDTH_PROC_H /* gdth_proc.h - * $Id: gdth_proc.h,v 1.2 1997/02/21 08:08:51 achim Exp $ + * $Id: gdth_proc.h,v 1.4 1998/06/10 12:20:34 achim Exp $ */ static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum); @@ -16,7 +16,7 @@ static void gdth_wait_completion(int hanum, int busnum, int id); static void gdth_stop_timeout(int hanum, int busnum, int id); static void gdth_start_timeout(int hanum, int busnum, int id); -static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout); +static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout); void gdth_scsi_done(Scsi_Cmnd *scp); diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v2.0.35/linux/drivers/scsi/hosts.c Sun Nov 15 10:49:44 1998 +++ linux/drivers/scsi/hosts.c Sun Nov 15 10:33:09 1998 @@ -161,6 +161,10 @@ #include "AM53C974.h" #endif +#ifdef CONFIG_SCSI_MEGARAID +#include "megaraid.h" +#endif + #ifdef CONFIG_SCSI_PPA #include "ppa.h" #endif @@ -312,6 +316,9 @@ #ifdef CONFIG_SCSI_AM53C974 AM53C974, #endif +#ifdef CONFIG_SCSI_MEGARAID + MEGARAID, +#endif #ifdef CONFIG_SCSI_PPA PPA, #endif @@ -357,10 +364,18 @@ /* If we are removing the last host registered, it is safe to reuse * its host number (this avoids "holes" at boot time) (DB) + * It is also safe to reuse those of numbers directly below which have + * been released earlier (to avoid some holes in numbering). */ - if (max_scsi_hosts == next_scsi_host) - max_scsi_hosts--; - + if(sh->host_no == max_scsi_hosts - 1) { + while(--max_scsi_hosts >= next_scsi_host) { + shpnt = scsi_hostlist; + while(shpnt && shpnt->host_no != max_scsi_hosts - 1) + shpnt = shpnt->next; + if(shpnt) + break; + } + } next_scsi_host--; scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes); } diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c --- v2.0.35/linux/drivers/scsi/in2000.c Mon Jul 13 13:46:35 1998 +++ linux/drivers/scsi/in2000.c Sun Nov 15 10:33:09 1998 @@ -122,28 +122,12 @@ #include "sd.h" #include "hosts.h" - -#define IN2000_VERSION "1.32" -#define IN2000_DATE "28/March/1998" - -/* - * Note - the following defines have been moved to 'in2000.h': - * - * PROC_INTERFACE - * PROC_STATISTICS - * SYNC_DEBUG - * DEBUGGING_ON - * DEBUG_DEFAULTS - * FAST_READ_IO - * FAST_WRITE_IO - * - */ - +#define IN2000_VERSION "1.33" +#define IN2000_DATE "26/August/1998" #include "in2000.h" - /* * 'setup_strings' is a single string used to pass operating parameters and * settings from the kernel/module command-line to the driver. 'setup_args[]' @@ -403,6 +387,10 @@ cmd->SCp.Status = ILLEGAL_STATUS_BYTE; +/* We need to disable interrupts before messing with the input + * queue and calling in2000_execute(). + */ + save_flags(flags); cli(); @@ -442,20 +430,19 @@ * already connected, we give up immediately. Otherwise, look through * the input_Q, using the first command we find that's intended * for a currently non-busy target/lun. + * Note that this function is always called with interrupts already + * disabled (either from in2000_queuecommand() or in2000_intr()). */ static void in2000_execute (struct Scsi_Host *instance) { struct IN2000_hostdata *hostdata; Scsi_Cmnd *cmd, *prev; -unsigned long flags; int i; unsigned short *sp; unsigned short f; unsigned short flushbuf[16]; - save_flags(flags); - cli(); hostdata = (struct IN2000_hostdata *)instance->hostdata; DB(DB_EXECUTE,printk("EX(")) @@ -464,7 +451,6 @@ DB(DB_EXECUTE,printk(")EX-0 ")) - restore_flags(flags); return; } @@ -488,7 +474,6 @@ DB(DB_EXECUTE,printk(")EX-1 ")) - restore_flags(flags); return; } @@ -716,7 +701,6 @@ DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid)) - restore_flags(flags); } @@ -841,15 +825,10 @@ } -/* It appears that the Linux interrupt dispatcher calls this - * function in a non-reentrant fashion. What that means to us - * is that we can use an SA_INTERRUPT type of interrupt (which - * is faster), and do an sti() right away to let timer, serial, - * etc. ints happen. - * - * WHOA! Wait a minute, pardner! Does this hold when more than - * one card has been detected?? I doubt it. Maybe better - * re-think the multiple card capability.... +/* We need to use spin_lock_irqsave() & spin_unlock_irqrestore() in this + * function in order to work in an SMP environment. (I'd be surprised + * if the driver is ever used by anyone on a real multi-CPU motherboard, + * but it _does_ need to be able to compile and run in an SMP kernel.) */ static void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs) @@ -857,12 +836,12 @@ struct Scsi_Host *instance; struct IN2000_hostdata *hostdata; Scsi_Cmnd *patch, *cmd; -unsigned long flags; uchar asr, sr, phs, id, lun, *ucp, msg; int i,j; unsigned long length; unsigned short *sp; unsigned short f; +unsigned long flags; for (instance = instance_list; instance; instance = instance->next) { if (instance->irq == irqnum) @@ -874,10 +853,9 @@ } hostdata = (struct IN2000_hostdata *)instance->hostdata; -/* OK - it should now be safe to re-enable system interrupts */ +/* Get the spin_lock and disable further ints, for SMP */ - save_flags(flags); - sti(); + CLISPIN_LOCK(flags); #ifdef PROC_STATISTICS hostdata->int_cnt++; @@ -1013,7 +991,9 @@ } write1_io(0, IO_LED_OFF); - restore_flags(flags); + +/* release the SMP spin_lock and restore irq state */ + CLISPIN_UNLOCK(flags); return; } @@ -1029,7 +1009,9 @@ if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) { printk("\nNR:wd-intr-1\n"); write1_io(0, IO_LED_OFF); - restore_flags(flags); + +/* release the SMP spin_lock and restore irq state */ + CLISPIN_UNLOCK(flags); return; } @@ -1095,7 +1077,6 @@ case CSR_TIMEOUT: DB(DB_INTR,printk("TIMEOUT")) - cli(); if (hostdata->state == S_RUNNING_LEVEL2) hostdata->connected = NULL; else { @@ -1113,7 +1094,6 @@ * are commands waiting to be executed. */ - sti(); in2000_execute(instance); break; @@ -1121,7 +1101,6 @@ /* Note: this interrupt should not occur in a LEVEL2 command */ case CSR_SELECT: - cli(); DB(DB_INTR,printk("SELECT")) hostdata->connected = cmd = (Scsi_Cmnd *)hostdata->selecting; CHECK_NULL(cmd,"csr_select") @@ -1211,7 +1190,6 @@ case CSR_SRV_REQ |PHS_MESS_IN: DB(DB_INTR,printk("MSG_IN=")) - cli(); msg = read_1_byte(hostdata); sr = read_3393(hostdata,WD_SCSI_STATUS); /* clear interrupt */ @@ -1360,7 +1338,6 @@ /* Note: this interrupt will occur only after a LEVEL2 command */ case CSR_SEL_XFER_DONE: - cli(); /* Make sure that reselection is enabled at this point - it may * have been turned off for the command that just completed. @@ -1387,7 +1364,6 @@ * there are commands waiting to be executed. */ - sti(); in2000_execute(instance); } else { @@ -1446,7 +1422,6 @@ * so we treat it as a normal command-complete-disconnect. */ - cli(); /* Make sure that reselection is enabled at this point - it may * have been turned off for the command that just completed. @@ -1456,6 +1431,9 @@ if (cmd == NULL) { printk(" - Already disconnected! "); hostdata->state = S_UNCONNECTED; + +/* release the SMP spin_lock and restore irq state */ + CLISPIN_UNLOCK(flags); return; } DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid)) @@ -1472,13 +1450,11 @@ * there are commands waiting to be executed. */ - sti(); in2000_execute(instance); break; case CSR_DISC: - cli(); /* Make sure that reselection is enabled at this point - it may * have been turned off for the command that just completed. @@ -1523,7 +1499,6 @@ * there are commands waiting to be executed. */ - sti(); in2000_execute(instance); break; @@ -1531,8 +1506,6 @@ case CSR_RESEL_AM: DB(DB_INTR,printk("RESEL")) - cli(); - /* First we have to make sure this reselection didn't */ /* happen during Arbitration/Selection of some other device. */ /* If yes, put losing command back on top of input_Q. */ @@ -1632,10 +1605,12 @@ } write1_io(0, IO_LED_OFF); - restore_flags(flags); DB(DB_INTR,printk("} ")) +/* release the SMP spin_lock and restore irq state */ + CLISPIN_UNLOCK(flags); + } @@ -1823,7 +1798,6 @@ cmd->result = DID_ABORT << 16; cmd->scsi_done(cmd); -/* sti();*/ in2000_execute (instance); restore_flags(flags); @@ -1854,7 +1828,6 @@ * broke. */ -/* sti();*/ in2000_execute (instance); restore_flags(flags); @@ -1872,7 +1845,7 @@ static char setup_used[MAX_SETUP_ARGS]; static int done_setup = 0; -void in2000_setup (char *str, int *ints) +in2000__INITFUNC( void in2000_setup (char *str, int *ints) ) { int i; char *p1,*p2; @@ -1904,7 +1877,7 @@ /* check_setup_args() returns index if key found, 0 if not */ -static int check_setup_args(char *key, int *flags, int *val, char *buf) +in2000__INITFUNC( static int check_setup_args(char *key, int *flags, int *val, char *buf) ) { int x; char *cp; @@ -1936,21 +1909,21 @@ * special macros declared in 'asm/io.h'. We use readb() and readl() * when reading from the card's BIOS area in in2000_detect(). */ -static const unsigned int *bios_tab[] = { +static const unsigned int *bios_tab[] in2000__INITDATA = { (unsigned int *)0xc8000, (unsigned int *)0xd0000, (unsigned int *)0xd8000, 0 }; -static const unsigned short base_tab[] = { +static const unsigned short base_tab[] in2000__INITDATA = { 0x220, 0x200, 0x110, 0x100, }; -static const int int_tab[] = { +static const int int_tab[] in2000__INITDATA = { 15, 14, 11, @@ -1958,7 +1931,7 @@ }; -int in2000_detect(Scsi_Host_Template * tpnt) +in2000__INITFUNC( int in2000_detect(Scsi_Host_Template * tpnt) ) { struct Scsi_Host *instance; struct IN2000_hostdata *hostdata; diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h --- v2.0.35/linux/drivers/scsi/in2000.h Mon Jul 13 13:46:35 1998 +++ linux/drivers/scsi/in2000.h Sun Nov 15 10:33:09 1998 @@ -2,7 +2,7 @@ * in2000.h - Linux device driver definitions for the * Always IN2000 ISA SCSI card. * - * IMPORTANT: This file is for version 1.32 - 28/Mar/1998 + * IMPORTANT: This file is for version 1.33 - 26/Aug/1998 * * Copyright (c) 1996 John Shifflett, GeoLog Consulting * john@geolog.com @@ -377,10 +377,29 @@ #define PR_STOP 1<<7 -int in2000_detect(Scsi_Host_Template *); +#include + +#if LINUX_VERSION_CODE < 0x020100 /* 2.0.xx */ +# define in2000__INITFUNC(function) function +# define in2000__INIT +# define in2000__INITDATA +# define CLISPIN_LOCK(flags) do { save_flags(flags); cli(); } while(0) +# define CLISPIN_UNLOCK(flags) restore_flags(flags) +#else /* 2.1.xxx */ +# include +# include +# define in2000__INITFUNC(function) __initfunc(function) +# define in2000__INIT __init +# define in2000__INITDATA __initdata +# define CLISPIN_LOCK(flags) spin_lock_irqsave(&io_request_lock, flags) +# define CLISPIN_UNLOCK(flags) spin_unlock_irqrestore(&io_request_lock, flags) +#endif + + +int in2000_detect(Scsi_Host_Template *) in2000__INIT; int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int in2000_abort(Scsi_Cmnd *); -void in2000_setup(char *, int *); +void in2000_setup(char *, int *) in2000__INIT; int in2000_proc_info(char *, char **, off_t, int, int, int); struct proc_dir_entry proc_scsi_in2000; int in2000_biosparam(struct scsi_disk *, kdev_t, int *); @@ -392,6 +411,8 @@ #define IN2000_CPL 2 #define IN2000_HOST_ID 7 +#if LINUX_VERSION_CODE < 0x020100 /* 2.0.xx */ + #define IN2000 { NULL, /* link pointer for modules */ \ NULL, /* usage_count for modules */ \ &proc_scsi_in2000, /* pointer to /proc/scsi directory entry */ \ @@ -414,6 +435,26 @@ 0, /* unchecked dma */ \ DISABLE_CLUSTERING \ } + +#else /* 2.1.xxx */ + +#define IN2000 { proc_dir: &proc_scsi_in2000, /* pointer to /proc/scsi directory entry */ \ + proc_info: in2000_proc_info, /* pointer to proc info function */ \ + name: "Always IN2000", /* device name */ \ + detect: in2000_detect, /* returns number of in2000's found */ \ + queuecommand: in2000_queuecommand, /* queue scsi command, don't wait */ \ + abort: in2000_abort, /* abort current command */ \ + reset: in2000_reset, /* reset scsi bus */ \ + bios_param: in2000_biosparam, /* figures out BIOS parameters for lilo, etc */ \ + can_queue: IN2000_CAN_Q, /* max commands we can queue up */ \ + this_id: IN2000_HOST_ID, /* host-adapter scsi id */ \ + sg_tablesize: IN2000_SG, /* scatter-gather table size */ \ + cmd_per_lun: IN2000_CPL, /* commands per lun */ \ + use_clustering: DISABLE_CLUSTERING, /* ENABLE_CLUSTERING may speed things up */ \ + use_new_eh_code: 0 /* new error code - not using it yet */ \ + } + +#endif #endif /* IN2000_H */ diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/megaraid.c linux/drivers/scsi/megaraid.c --- v2.0.35/linux/drivers/scsi/megaraid.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/megaraid.c Sun Nov 15 10:33:09 1998 @@ -0,0 +1,1347 @@ +/*=================================================================== + * + * Linux MegaRAID device driver + * + * Copyright 1998 American Megatrends Inc. + * + * 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. + * + * Version : 0.92 + * + * Description: Linux device driver for AMI MegaRAID controller + * + * History: + * + * Version 0.90: + * Works and has been tested with the MegaRAID 428 controller, and + * the MegaRAID 438 controller. Probably works with the 466 also, + * but not tested. + * + * Version 0.91: + * Aligned mailbox area on 16-byte boundry. + * Added schedule() at the end to properly clean up. + * Made improvements for conformity to linux driver standards. + * + * Version 0.92: + * Added support for 2.1 kernels. + * Reads from pci_dev struct, so it's not dependent on pcibios. + * Added some missing virt_to_bus() translations. + * Added support for SMP. + * Changed global cli()'s to spinlocks for 2.1, and simulated + * spinlocks for 2.0. + * Removed setting of SA_INTERRUPT flag when requesting Irq. + * + * Version 0.92ac: + * Small changes to the comments/formatting. Plus a couple of + * added notes. Returned to the authors. No actual code changes + * save printk levels. + * 8 Oct 98 Alan Cox + * + * BUGS: + * Tested with 2.1.90, but unfortunately there is a bug in pci.c which + * fails to detect our controller. Does work with 2.1.118--don't know + * which kernel in between it was fixed in. + * With SMP enabled under 2.1.118 with more than one processor, gets an + * error message "scsi_end_request: buffer-list destroyed" under heavy + * IO, but doesn't seem to affect operation, or data integrity. The + * message doesn't occur without SMP enabled, or with one proccessor with + * SMP enabled, or under any combination under 2.0 kernels. + * + *===================================================================*/ +#define QISR 1 + +#define CRLFSTR "\n" + +#define MULTIQ 1 + +#include +#include + +#ifdef MODULE +#include + +#if LINUX_VERSION_CODE >= 0x20100 +char kernel_version[] = UTS_RELEASE; + +/* originally ported by Dell Corporation; updated, released, and maintained by + American Megatrends */ +MODULE_AUTHOR("American Megatrends Inc."); +MODULE_DESCRIPTION("AMI MegaRAID driver"); +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* for kmalloc() */ +#include /* for CONFIG_PCI */ +#if LINUX_VERSION_CODE < 0x20100 +#include +#else +#include +#endif + +#include +#include + +#include "sd.h" +#include "scsi.h" +#include "hosts.h" + +#include "megaraid.h" + +/*================================================================ + * + * #Defines + * + *================================================================*/ + +#if LINUX_VERSION_CODE < 0x020100 +#define ioremap vremap +#define iounmap vfree + +/* simulate spin locks */ +typedef struct {volatile char lock;} spinlock_t; +#define spin_lock_init(x) { (x)->lock = 0;} +#define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\ + (x)->lock=1; save_flags(flags);\ + cli();} +#define spin_unlock_irqrestore(x,flags) { (x)->lock=0; restore_flags(flags);} + +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +#define queue_task_irq(a,b) queue_task(a,b) +#define queue_task_irq_off(a,b) queue_task(a,b) +#endif + +#define MAX_SERBUF 160 +#define COM_BASE 0x2f8 + +#define ENQUEUE(obj,type,list,next) \ +{ type **node; long cpuflag; \ + spin_lock_irqsave(&mega_lock,cpuflag);\ + for(node=&(list); *node; node=(type **)&(*node)->##next); \ + (*node) = obj; \ + (*node)->##next = NULL; \ + spin_unlock_irqrestore(&mega_lock,cpuflag);\ +}; + +#define DEQUEUE(obj,type,list,next) \ +{ long cpuflag; \ + spin_lock_irqsave(&mega_lock,cpuflag);\ + if ((obj=list) != NULL) {\ + list = (type *)(list)->##next; \ + } \ + spin_unlock_irqrestore(&mega_lock,cpuflag);\ +}; + +u_long RDINDOOR(mega_host_config *megaCfg) +{ + return readl(megaCfg->base + 0x20); +} + +void WRINDOOR(mega_host_config *megaCfg, u_long value) +{ + writel(value,megaCfg->base+0x20); +} + +u_long RDOUTDOOR(mega_host_config *megaCfg) +{ + return readl(megaCfg->base+0x2C); +} + +void WROUTDOOR(mega_host_config *megaCfg, u_long value) +{ + writel(value,megaCfg->base+0x2C); +} + +/*================================================================ + * + * Function prototypes + * + *================================================================*/ +static int MegaIssueCmd(mega_host_config *megaCfg, + u_char *mboxData, + mega_scb *scb, + int intr); +static int build_sglist(mega_host_config *megaCfg, mega_scb *scb, + u_long *buffer, u_long *length); + +static void mega_runque(void *); +static void mega_rundoneq(void); +static void mega_cmd_done(mega_host_config *,mega_scb *, int); + +/* set SERDEBUG to 1 to enable serial debugging */ +#define SERDEBUG 0 +#if SERDEBUG +static void ser_init(void); +static void ser_puts(char *str); +static void ser_putc(char c); +static int ser_printk(const char *fmt, ...); +#endif + +/*================================================================ + * + * Global variables + * + *================================================================*/ +static int numCtlrs = 0; +static mega_host_config *megaCtlrs[4] = { 0 }; + +/* Change this to 0 if you want to see the raw drives */ +static int use_raid = 1; + +/* Queue of pending/completed SCBs */ +static mega_scb *qPending = NULL; +static Scsi_Cmnd *qCompleted = NULL; + +volatile static spinlock_t mega_lock; +static struct tq_struct runq = {0,0,mega_runque,NULL}; + +struct proc_dir_entry proc_scsi_megaraid = { + PROC_SCSI_MEGARAID, 8, "megaraid", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +#if SERDEBUG +static char strbuf[MAX_SERBUF+1]; + +static void ser_init() +{ + unsigned port=COM_BASE; + + outb(0x80,port+3); + outb(0,port+1); + /* 9600 Baud, if 19200: outb(6,port) */ + outb(12, port); + outb(3,port+3); + outb(0,port+1); +} + +static void ser_puts(char *str) +{ + char *ptr; + + ser_init(); + for (ptr=str;*ptr;++ptr) + ser_putc(*ptr); +} + +static void ser_putc(char c) +{ + unsigned port=COM_BASE; + + while ((inb(port+5) & 0x20)==0); + outb(c,port); + if (c==0x0a) + { + while ((inb(port+5) & 0x20)==0); + outb(0x0d,port); + } +} + +static int ser_printk(const char *fmt, ...) +{ + va_list args; + int i; + long flags; + + spin_lock_irqsave(mega_lock,flags); + va_start(args,fmt); + i = vsprintf(strbuf,fmt,args); + ser_puts(strbuf); + va_end(args); + spin_unlock_irqrestore(&mega_lock,flags); + + return i; +} + +#define TRACE(a) { ser_printk a;} + +#else +#define TRACE(A) +#endif + +void callDone(Scsi_Cmnd *SCpnt) +{ + if (SCpnt->result) { + TRACE(("*** %.08lx %.02x <%d.%d.%d> = %x\n", SCpnt->serial_number, + SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun, + SCpnt->result)); + } + SCpnt->scsi_done(SCpnt); +} + +/*------------------------------------------------------------------------- + * + * Local functions + * + *-------------------------------------------------------------------------*/ + +/*================================================ + * Initialize SCB structures + *================================================*/ +static void initSCB(mega_host_config *megaCfg) +{ + int idx; + + for(idx=0; idxmax_cmds; idx++) { + megaCfg->scbList[idx].idx = -1; + megaCfg->scbList[idx].flag = 0; + megaCfg->scbList[idx].sgList = NULL; + megaCfg->scbList[idx].SCpnt = NULL; + } +} + +/*=========================== + * Allocate a SCB structure + *===========================*/ +static mega_scb *allocateSCB(mega_host_config *megaCfg,Scsi_Cmnd *SCpnt) +{ + int idx; + long flags; + + spin_lock_irqsave(&mega_lock,flags); + for(idx=0; idxmax_cmds; idx++) { + if (megaCfg->scbList[idx].idx < 0) { + + /* Set Index and SCB pointer */ + megaCfg->scbList[idx].flag = 0; + megaCfg->scbList[idx].idx = idx; + megaCfg->scbList[idx].SCpnt = SCpnt; + megaCfg->scbList[idx].next = NULL; + spin_unlock_irqrestore(&mega_lock,flags); + + if (megaCfg->scbList[idx].sgList == NULL) { + megaCfg->scbList[idx].sgList = + kmalloc(sizeof(mega_sglist)*MAX_SGLIST,GFP_ATOMIC|GFP_DMA); + } + + return &megaCfg->scbList[idx]; + } + } + spin_unlock_irqrestore(&mega_lock,flags); + + printk(KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n"); + + return NULL; +} + +/*======================= + * Free a SCB structure + *=======================*/ +static void freeSCB(mega_scb *scb) +{ + long flags; + + spin_lock_irqsave(&mega_lock,flags); + scb->flag = 0; + scb->idx = -1; + scb->next = NULL; + scb->SCpnt = NULL; + spin_unlock_irqrestore(&mega_lock,flags); +} + +/* Run through the list of completed requests */ +static void mega_rundoneq() +{ + mega_host_config *megaCfg; + Scsi_Cmnd *SCpnt; + long islogical; + + while(1) { + DEQUEUE(SCpnt, Scsi_Cmnd, qCompleted, host_scribble); + if (SCpnt == NULL) return; + + megaCfg = (mega_host_config *)SCpnt->host->hostdata; + + /* Check if we're allowing access to RAID drives or physical + * if use_raid == 1 and this wasn't a disk on the max channel or + * if use_raid == 0 and this was a disk on the max channel + * then fail. + */ + islogical = (SCpnt->channel == megaCfg->host->max_channel) ? 1 : 0; + if (SCpnt->cmnd[0] == INQUIRY && + ((((u_char*)SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) && + (islogical != use_raid)) { + SCpnt->result = 0xF0; + } + + /* Convert result to error */ + switch(SCpnt->result) { + case 0x00: case 0x02: + SCpnt->result |= (DID_OK << 16); + break; + case 0x8: + SCpnt->result |= (DID_BUS_BUSY << 16); + break; + default: + SCpnt->result |= (DID_BAD_TARGET << 16); + break; + } + + /* Callback */ + callDone(SCpnt); + } +} + +/* Add command to the list of completed requests */ +static void mega_cmd_done(mega_host_config *megaCfg,mega_scb *pScb, int status) +{ + pScb->SCpnt->result = status; + ENQUEUE(pScb->SCpnt, Scsi_Cmnd, qCompleted, host_scribble); + freeSCB(pScb); +} + +/*---------------------------------------------------- + * Process pending queue list + * + * Run as a scheduled task + *----------------------------------------------------*/ +static void mega_runque(void *dummy) +{ + mega_host_config *megaCfg; + mega_scb *pScb; + long flags; + + /* Take care of any completed requests */ + mega_rundoneq(); + + DEQUEUE(pScb,mega_scb,qPending,next); + + if (pScb) { + megaCfg = (mega_host_config *)pScb->SCpnt->host->hostdata; + + if (megaCfg->mbox->busy || megaCfg->flag & (IN_ISR|PENDING)) { + printk(KERN_DEBUG "PENDING = %x, IN_ISR = %x, mbox.busy = %x\n",(u_int)(megaCfg->flag + & PENDING), (u_int)(megaCfg->flag & IN_ISR), megaCfg->mbox->busy); + TRACE(("%.08lx %.02x <%d.%d.%d> intr%d busy%d isr%d pending%d\n", + pScb->SCpnt->serial_number, + pScb->SCpnt->cmnd[0], + pScb->SCpnt->channel, + pScb->SCpnt->target, + pScb->SCpnt->lun, + intr_count, + megaCfg->mbox->busy, + (megaCfg->flag & IN_ISR) ? 1 : 0, + (megaCfg->flag & PENDING) ? 1 : 0)); + } + + if (MegaIssueCmd(megaCfg, pScb->mboxData, pScb, 1)) { + printk(KERN_DEBUG "MegaIssueCmd returned BUSY. Rescheduling command.\n"); + /* We're BUSY... come back later */ + spin_lock_irqsave(&mega_lock,flags); + pScb->next = qPending; + qPending = pScb; + spin_unlock_irqrestore(&mega_lock,flags); + + if (!(megaCfg->flag & PENDING)) { /* If PENDING, irq will schedule task */ + queue_task(&runq, &tq_scheduler); + } + } + } +} + +/*------------------------------------------------------------------- + * + * Build a SCB from a Scsi_Cmnd + * + * Returns a SCB pointer, or NULL + * If NULL is returned, the scsi_done function MUST have been called + * + *-------------------------------------------------------------------*/ +static mega_scb *mega_build_cmd(mega_host_config *megaCfg, Scsi_Cmnd *SCpnt) +{ + mega_scb *pScb; + mega_mailbox *mbox; + mega_passthru *pthru; + long seg; + + /* We don't support multi-luns */ + if (SCpnt->lun != 0) { + SCpnt->result = (DID_BAD_TARGET << 16); + callDone(SCpnt); + return NULL; + } + + /*----------------------------------------------------- + * + * Logical drive commands + * + *-----------------------------------------------------*/ + if (SCpnt->channel == megaCfg->host->max_channel) { + switch(SCpnt->cmnd[0]) { + case TEST_UNIT_READY: + memset(SCpnt->request_buffer, 0, SCpnt->request_bufflen); + SCpnt->result = (DID_OK << 16); + callDone(SCpnt); + return NULL; + + case MODE_SENSE: + memset(SCpnt->request_buffer, 0, SCpnt->cmnd[4]); + SCpnt->result = (DID_OK << 16); + callDone(SCpnt); + return NULL; + + case READ_CAPACITY: + case INQUIRY: + /* Allocate a SCB and initialize passthru */ + if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) { + SCpnt->result = (DID_ERROR << 16); + callDone(SCpnt); + return NULL; + } + pthru = &pScb->pthru; + mbox = (mega_mailbox *)&pScb->mboxData; + + memset(mbox, 0, sizeof(pScb->mboxData)); + memset(pthru, 0, sizeof(mega_passthru)); + pthru->timeout = 0; + pthru->ars = 0; + pthru->islogical = 1; + pthru->logdrv = SCpnt->target; + pthru->cdblen = SCpnt->cmd_len; + pthru->dataxferaddr = virt_to_bus(SCpnt->request_buffer); + pthru->dataxferlen = SCpnt->request_bufflen; + memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len); + + /* Initialize mailbox area */ + mbox->cmd = MEGA_MBOXCMD_PASSTHRU; + mbox->xferaddr = virt_to_bus(pthru); + + return pScb; + + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + /* Allocate a SCB and initialize mailbox */ + if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) { + SCpnt->result = (DID_ERROR << 16); + callDone(SCpnt); + return NULL; + } + mbox = (mega_mailbox *)&pScb->mboxData; + + memset(mbox, 0, sizeof(pScb->mboxData)); + mbox->logdrv = SCpnt->target; + 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]; + mbox->lba = + ((u_long)SCpnt->cmnd[1] << 16) | + ((u_long)SCpnt->cmnd[2] << 8) | + (u_long)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); + 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]; + } + + /* Calculate Scatter-Gather info */ + mbox->numsgelements = build_sglist(megaCfg, pScb, + (u_long*)&mbox->xferaddr, + (u_long*)&seg); + + return pScb; + + default: + SCpnt->result = (DID_BAD_TARGET << 16); + callDone(SCpnt); + return NULL; + } + } + /*----------------------------------------------------- + * + * Passthru drive commands + * + *-----------------------------------------------------*/ + else { + /* Allocate a SCB and initialize passthru */ + if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) { + SCpnt->result = (DID_ERROR << 16); + callDone(SCpnt); + return NULL; + } + pthru = &pScb->pthru; + mbox = (mega_mailbox *)pScb->mboxData; + + memset(mbox, 0, sizeof(pScb->mboxData)); + memset(pthru, 0, sizeof(mega_passthru)); + pthru->timeout = 0; + pthru->ars = 0; + pthru->islogical = 0; + pthru->channel = SCpnt->channel; + pthru->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); + + /* Initialize mailbox */ + mbox->cmd = MEGA_MBOXCMD_PASSTHRU; + mbox->xferaddr = virt_to_bus(pthru); + + return pScb; + } + return NULL; +} + +/*-------------------------------------------------------------------- + * Interrupt service routine + *--------------------------------------------------------------------*/ +static void megaraid_isr(int irq, void *devp, struct pt_regs *regs) +{ + mega_host_config *megaCfg; + u_char byte, idx, sIdx; + u_long dword; + mega_mailbox *mbox; + mega_scb *pScb; + long flags; + int qCnt, qStatus; + + megaCfg = (mega_host_config *)devp; + mbox = (mega_mailbox *)megaCfg->mbox; + + if (megaCfg->host->irq == irq) { + spin_lock_irqsave(&mega_lock,flags); + + if (megaCfg->flag & IN_ISR) { + TRACE(("ISR called reentrantly!!\n")); + } + + megaCfg->flag |= IN_ISR; + + /* Check if a valid interrupt is pending */ + if (megaCfg->flag & BOARD_QUARTZ) { + dword = RDOUTDOOR(megaCfg); + if (dword != 0x10001234) { + /* Spurious interrupt */ + megaCfg->flag &= ~IN_ISR; + spin_unlock_irqrestore(&mega_lock,flags); + return; + } + WROUTDOOR(megaCfg,dword); + } else { + byte = READ_PORT(megaCfg->host->io_port, INTR_PORT); + if ((byte & VALID_INTR_BYTE) == 0) { + /* Spurious interrupt */ + megaCfg->flag &= ~IN_ISR; + spin_unlock_irqrestore(&mega_lock,flags); + return; + } + WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte); + } + + qCnt = mbox->numstatus; + qStatus = mbox->status; + + if (qCnt > 1) {TRACE(("ISR: Received %d status\n", qCnt)) + printk(KERN_DEBUG "Got numstatus = %d\n",qCnt); + } + + for(idx=0; idxcompleted[idx]; + if (sIdx > 0) { + pScb = &megaCfg->scbList[sIdx-1]; + spin_unlock_irqrestore(&mega_lock,flags); /* locks within cmd_done */ + mega_cmd_done(megaCfg,&megaCfg->scbList[sIdx-1], qStatus); + spin_lock_irqsave(&mega_lock,flags); + } + } + if (megaCfg->flag & BOARD_QUARTZ) { + WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox)|0x2); + while (RDINDOOR(megaCfg) & 0x02); + } else { + CLEAR_INTR(megaCfg->host->io_port); + } + + megaCfg->flag &= ~IN_ISR; + megaCfg->flag &= ~PENDING; + + /* Queue as a delayed ISR routine */ + queue_task_irq_off(&runq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + spin_unlock_irqrestore(&mega_lock,flags); + + } +} + +/*==================================================*/ +/* Wait until the controller's mailbox is available */ +/*==================================================*/ +static int busyWaitMbox(mega_host_config *megaCfg) +{ + mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox; + long counter; + + for(counter=0; counter<0xFFFFFF; counter++) { + if (!mbox->busy) return 0; + } + return -1; +} + +/*===================================================== + * Post a command to the card + * + * Arguments: + * mega_host_config *megaCfg - Controller structure + * 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 + *=====================================================*/ +static int MegaIssueCmd(mega_host_config *megaCfg, + u_char *mboxData, + mega_scb *pScb, + int intr) +{ + mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox; + long flags; + u_char byte; + u_long cmdDone; + + mboxData[0x1] = (pScb ? pScb->idx+1 : 0x00); /* Set cmdid */ + mboxData[0xF] = 1; /* Set busy */ + + /* one bad report of problem when issuing a command while pending. + * Wasn't able to duplicate, but it doesn't really affect performance + * anyway, so don't allow command while PENDING + */ + if (megaCfg->flag & PENDING) { + return -1; + } + + /* Wait until mailbox is free */ + if (busyWaitMbox(megaCfg)) { + if (pScb) { + TRACE(("Mailbox busy %.08lx <%d.%d.%d>\n", pScb->SCpnt->serial_number, + pScb->SCpnt->channel, pScb->SCpnt->target, pScb->SCpnt->lun)); + } + return -1; + } + + /* Copy mailbox data into host structure */ + spin_lock_irqsave(&mega_lock,flags); + memset(mbox, 0, sizeof(mega_mailbox)); + memcpy(mbox, mboxData, 16); + spin_unlock_irqrestore(&mega_lock,flags); + + /* Kick IO */ + megaCfg->flag |= PENDING; + if (intr) { + /* Issue interrupt (non-blocking) command */ + if (megaCfg->flag & BOARD_QUARTZ) { + mbox->mraid_poll = 0; + mbox->mraid_ack = 0; + WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1); + } else { + ENABLE_INTR(megaCfg->host->io_port); + ISSUE_COMMAND(megaCfg->host->io_port); + } + } + else { /* Issue non-ISR (blocking) command */ + + if (megaCfg->flag & BOARD_QUARTZ) { + + mbox->mraid_poll = 0; + mbox->mraid_ack = 0; + WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1); + + while((cmdDone=RDOUTDOOR(megaCfg)) != 0x10001234); + WROUTDOOR(megaCfg, cmdDone); + + if (pScb) { + mega_cmd_done(megaCfg,pScb, mbox->status); + mega_rundoneq(); + } + + WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox) | 0x2); + while(RDINDOOR(megaCfg) & 0x2); + + megaCfg->flag &= ~PENDING; + } + else { + DISABLE_INTR(megaCfg->host->io_port); + ISSUE_COMMAND(megaCfg->host->io_port); + + while(!((byte=READ_PORT(megaCfg->host->io_port,INTR_PORT))&INTR_VALID)); + WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte); + + ENABLE_INTR(megaCfg->host->io_port); + CLEAR_INTR(megaCfg->host->io_port); + + if (pScb) { + mega_cmd_done(megaCfg,pScb, mbox->status); + mega_rundoneq(); + } + megaCfg->flag &= ~PENDING; + } + } + + return 0; +} + +/*------------------------------------------------------------------- + * Copies data to SGLIST + *-------------------------------------------------------------------*/ +static int build_sglist(mega_host_config *megaCfg, mega_scb *scb, + u_long *buffer, u_long *length) +{ + struct scatterlist *sgList; + int idx; + + /* 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; + return 0; + } + + sgList = (struct scatterlist *)scb->SCpnt->buffer; + if (scb->SCpnt->use_sg == 1) { + *buffer = virt_to_bus(sgList[0].address); + *length = (u_long)sgList[0].length; + return 0; + } + + /* Copy Scatter-Gather list info into controller structure */ + for(idx=0; idxSCpnt->use_sg; idx++) { + scb->sgList[idx].address = virt_to_bus(sgList[idx].address); + scb->sgList[idx].length = (u_long)sgList[idx].length; + } + + /* Reset pointer and length fields */ + *buffer = virt_to_bus(scb->sgList); + *length = 0; + + /* Return count of SG requests */ + return scb->SCpnt->use_sg; +} + +/*-------------------------------------------------------------------- + * Initializes the adress of the controller's mailbox register + * The mailbox register is used to issue commands to the card. + * Format of the mailbox area: + * 00 01 command + * 01 01 command id + * 02 02 # of sectors + * 04 04 logical bus address + * 08 04 physical buffer address + * 0C 01 logical drive # + * 0D 01 length of scatter/gather list + * 0E 01 reserved + * 0F 01 mailbox busy + * 10 01 numstatus byte + * 11 01 status byte + *--------------------------------------------------------------------*/ +static int mega_register_mailbox(mega_host_config *megaCfg, u_long paddr) +{ + /* align on 16-byte boundry */ + megaCfg->mbox = &megaCfg->mailbox; + megaCfg->mbox = (mega_mailbox *) ((((ulong)megaCfg->mbox) + 16)&0xfffffff0); + paddr = (paddr+16)&0xfffffff0; + + /* Register mailbox area with the firmware */ + if (megaCfg->flag & BOARD_QUARTZ) { + } + else { + WRITE_PORT(megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF); + WRITE_PORT(megaCfg->host->io_port, MBOX_PORT1, (paddr >> 8) & 0xFF); + WRITE_PORT(megaCfg->host->io_port, MBOX_PORT2, (paddr >> 16) & 0xFF); + WRITE_PORT(megaCfg->host->io_port, MBOX_PORT3, (paddr >> 24) & 0xFF); + WRITE_PORT(megaCfg->host->io_port, ENABLE_MBOX_REGION, ENABLE_MBOX_BYTE); + + CLEAR_INTR(megaCfg->host->io_port); + ENABLE_INTR(megaCfg->host->io_port); + } + return 0; +} + +/*------------------------------------------------------------------- + * Issue an adapter info query to the controller + *-------------------------------------------------------------------*/ +static int mega_i_query_adapter(mega_host_config *megaCfg) +{ + mega_RAIDINQ *adapterInfo; + mega_mailbox *mbox; + u_char mboxData[16]; + u_long paddr; + + spin_lock_init(&mega_lock); + /* Initialize adapter inquiry */ + 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; + + /* Issue a blocking command to the card */ + MegaIssueCmd(megaCfg, mboxData, NULL, 0); + + /* 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->numldrv = adapterInfo->LogdrvInfo.NumLDrv; + +#if 0 + printk(KERN_DEBUG "---- Logical drive info ----\n"); + for(i=0; inumldrv; i++) { + printk(KERN_DEBUG "%d: size: %ld prop: %x state: %x\n",i, + adapterInfo->LogdrvInfo.LDrvSize[i], + adapterInfo->LogdrvInfo.LDrvProp[i], + adapterInfo->LogdrvInfo.LDrvState[i]); + } + printk(KERN_DEBUG "---- Physical drive info ----\n"); + for(i=0; iPhysdrvInfo.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); + 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); +#else + memcpy(megaCfg->fwVer, adapterInfo->AdpInfo.FwVer, 4); + megaCfg->fwVer[4] = 0; + + memcpy(megaCfg->biosVer, adapterInfo->AdpInfo.BiosVer, 4); + megaCfg->biosVer[4] = 0; +#endif + + printk(KERN_INFO "megaraid: [%s:%s] detected %d logical drives" CRLFSTR, + megaCfg->fwVer, + megaCfg->biosVer, + megaCfg->numldrv); + return 0; +} + +/*------------------------------------------------------------------------- + * + * Driver interface functions + * + *-------------------------------------------------------------------------*/ + +/*---------------------------------------------------------- + * Returns data to be displayed in /proc/scsi/megaraid/X + *----------------------------------------------------------*/ +int megaraid_proc_info(char *buffer, char **start, off_t offset, + int length, int inode, int inout) +{ + *start = buffer; + return 0; +} + +int findCard(Scsi_Host_Template *pHostTmpl, + u_short pciVendor, u_short pciDev, + long flag) +{ + mega_host_config *megaCfg; + struct Scsi_Host *host; + u_char pciBus, pciDevFun, megaIrq; + u_long megaBase; + u_short pciIdx = 0; + +#if LINUX_VERSION_CODE < 0x20100 + while(!pcibios_find_device(pciVendor, pciDev, pciIdx,&pciBus,&pciDevFun)) { +#else + struct pci_dev *pdev=pci_devices; + + while((pdev = pci_find_device(pciVendor, pciDev, pdev))) { + pciBus = pdev->bus->number; + pciDevFun = pdev->devfn; +#endif + printk(KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:fun %d\n", + pciVendor, + pciDev, + pciIdx, pciBus, + PCI_SLOT(pciDevFun), + PCI_FUNC(pciDevFun)); + + /* Read the base port and IRQ from PCI */ +#if LINUX_VERSION_CODE < 0x20100 + pcibios_read_config_dword(pciBus, pciDevFun, + PCI_BASE_ADDRESS_0, + (u_int *)&megaBase); + pcibios_read_config_byte(pciBus, pciDevFun, + PCI_INTERRUPT_LINE, + &megaIrq); +#else + megaBase = pdev->base_address[0]; + megaIrq = pdev->irq; +#endif + pciIdx++; + + if (flag & BOARD_QUARTZ) { + megaBase &= PCI_BASE_ADDRESS_MEM_MASK; + megaBase = (long) ioremap(megaBase,128); + } + else { + megaBase &= PCI_BASE_ADDRESS_IO_MASK; + megaBase += 0x10; + } + + /* Initialize SCSI Host structure */ + host = scsi_register(pHostTmpl, sizeof(mega_host_config)); + megaCfg = (mega_host_config *)host->hostdata; + memset(megaCfg, 0, sizeof(mega_host_config)); + + printk(KERN_INFO " scsi%d: Found a MegaRAID controller at 0x%x, IRQ: %d" CRLFSTR, + host->host_no, (u_int)megaBase, megaIrq); + + /* Copy resource info into structure */ + megaCfg->flag = flag; + megaCfg->host = host; + megaCfg->base = megaBase; + megaCfg->host->irq = megaIrq; + megaCfg->host->io_port = megaBase; + megaCfg->host->n_io_port = 16; + megaCfg->host->unique_id = (pciBus << 8) | pciDevFun; + megaCtlrs[numCtlrs++] = megaCfg; + + if (flag != BOARD_QUARTZ) { + /* Request our IO Range */ + if (check_region(megaBase, 16)) { + printk(KERN_WARNING "megaraid: Couldn't register I/O range!" CRLFSTR); + scsi_unregister(host); + continue; + } + request_region(megaBase, 16, "megaraid"); + } + + /* Request our IRQ */ + if (request_irq(megaIrq, megaraid_isr, SA_SHIRQ, + "megaraid", megaCfg)) { + printk(KERN_WARNING "megaraid: Couldn't register IRQ %d!" CRLFSTR, + megaIrq); + scsi_unregister(host); + continue; + } + + mega_register_mailbox(megaCfg, virt_to_bus((void*)&megaCfg->mailbox)); + mega_i_query_adapter(megaCfg); + + /* Initialize SCBs */ + initSCB(megaCfg); + + } + return pciIdx; +} + +/*--------------------------------------------------------- + * Detects if a megaraid controller exists in this system + *---------------------------------------------------------*/ +int megaraid_detect(Scsi_Host_Template *pHostTmpl) +{ + int count = 0; + + pHostTmpl->proc_dir = &proc_scsi_megaraid; + +#if LINUX_VERSION_CODE < 0x20100 + if (!pcibios_present()) + { + printk(KERN_WARNING "megaraid: PCI bios not present." CRLFSTR); + return 0; + } +#endif + + count += findCard(pHostTmpl, 0x101E, 0x9010, 0); + count += findCard(pHostTmpl, 0x101E, 0x9060, 0); + count += findCard(pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ); + + return count; +} + +/*--------------------------------------------------------------------- + * Release the controller's resources + *---------------------------------------------------------------------*/ +int megaraid_release(struct Scsi_Host *pSHost) +{ + mega_host_config *megaCfg; + mega_mailbox *mbox; + u_char mboxData[16]; + + megaCfg = (mega_host_config*)pSHost->hostdata; + mbox = (mega_mailbox *)mboxData; + + /* Flush cache to disk */ + memset(mbox, 0, 16); + mboxData[0] = 0xA; + + /* Issue a blocking (interrupts disabled) command to the card */ + MegaIssueCmd(megaCfg, mboxData, NULL, 0); + + schedule(); + + /* Free our resources */ + if (megaCfg->flag & BOARD_QUARTZ) { + iounmap((void *)megaCfg->base); + } else { + release_region(megaCfg->host->io_port, 16); + } + free_irq(megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise + extra interrupt is generated */ + scsi_unregister(pSHost); + + return 0; +} + +/*---------------------------------------------- + * Get information about the card/driver + *----------------------------------------------*/ +const char *megaraid_info(struct Scsi_Host *pSHost) +{ + 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", + megaCfg->fwVer, + adapterInfo->AdpInfo.MaxConcCmds, + megaCfg->host->max_id, + megaCfg->host->max_channel); + return buffer; +} + +/*----------------------------------------------------------------- + * Perform a SCSI command + * Mailbox area: + * 00 01 command + * 01 01 command id + * 02 02 # of sectors + * 04 04 logical bus address + * 08 04 physical buffer address + * 0C 01 logical drive # + * 0D 01 length of scatter/gather list + * 0E 01 reserved + * 0F 01 mailbox busy + * 10 01 numstatus byte + * 11 01 status byte + *-----------------------------------------------------------------*/ +int megaraid_queue(Scsi_Cmnd *SCpnt, void (*pktComp)(Scsi_Cmnd *)) +{ + mega_host_config *megaCfg; + mega_scb *pScb; + + megaCfg = (mega_host_config *)SCpnt->host->hostdata; + + if (!(megaCfg->flag & (1L << SCpnt->channel))) { + printk(KERN_INFO "scsi%d: scanning channel %c for devices.\n", + megaCfg->host->host_no, + SCpnt->channel + 'A'); + megaCfg->flag |= (1L << SCpnt->channel); + } + + SCpnt->scsi_done = pktComp; + + /* Allocate and build a SCB request */ + if ((pScb = mega_build_cmd(megaCfg, SCpnt)) != NULL) { + /* Add SCB to the head of the pending queue */ + ENQUEUE(pScb, mega_scb, qPending, next); + + /* Issue the command to the card */ + mega_runque(NULL); + } + + return 0; +} + +/*---------------------------------------------------------------------- + * Issue a blocking command to the controller + * + * Note - this isnt 2.0.x SMP safe + *----------------------------------------------------------------------*/ +volatile static int internal_done_flag = 0; +volatile static int internal_done_errcode = 0; + +static void internal_done(Scsi_Cmnd *SCpnt) +{ + internal_done_errcode = SCpnt->result; + internal_done_flag++; +} + +/* + * This seems dangerous in an SMP environment because + * while spinning on internal_done_flag in 2.0.x SMP + * no IRQ's will be taken, including those that might + * be needed to clear this. + * + * I think this should be using a wait queue ? + * -- AC + */ + +int megaraid_command(Scsi_Cmnd *SCpnt) +{ + internal_done_flag = 0; + + /* Queue command, and wait until it has completed */ + megaraid_queue(SCpnt, internal_done); + + while(!internal_done_flag) + barrier(); + + return internal_done_errcode; +} + +/*--------------------------------------------------------------------- + * Abort a previous SCSI request + *---------------------------------------------------------------------*/ +int megaraid_abort(Scsi_Cmnd *SCpnt) +{ + mega_host_config *megaCfg; + int idx; + long flags; + + spin_lock_irqsave(&mega_lock,flags); + + megaCfg = (mega_host_config *)SCpnt->host->hostdata; + + TRACE(("ABORT!!! %.08lx %.02x <%d.%d.%d>\n", + SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, + SCpnt->lun)); + /* + * Walk list of SCBs for any that are still outstanding + */ + for(idx=0; idxmax_cmds; idx++) { + if (megaCfg->scbList[idx].idx >= 0) { + if (megaCfg->scbList[idx].SCpnt == SCpnt) { + freeSCB(&megaCfg->scbList[idx]); + + SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24); + callDone(SCpnt); + } + } + } + spin_unlock_irqrestore(&mega_lock,flags); + return SCSI_ABORT_SNOOZE; +} + +/*--------------------------------------------------------------------- + * Reset a previous SCSI request + *---------------------------------------------------------------------*/ +int megaraid_reset(Scsi_Cmnd *SCpnt, unsigned int rstflags) +{ + mega_host_config *megaCfg; + int idx; + long flags; + + spin_lock_irqsave(&mega_lock,flags); + + megaCfg = (mega_host_config *)SCpnt->host->hostdata; + + TRACE(("RESET: %.08lx %.02x <%d.%d.%d>\n", + SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, + SCpnt->lun)); + + /* + * Walk list of SCBs for any that are still outstanding + */ + for(idx=0; idxmax_cmds; idx++) { + if (megaCfg->scbList[idx].idx >= 0) { + SCpnt = megaCfg->scbList[idx].SCpnt; + freeSCB(&megaCfg->scbList[idx]); + SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24); + callDone(SCpnt); + } + } + spin_unlock_irqrestore(&mega_lock,flags); + return SCSI_RESET_PUNT; +} + +/*------------------------------------------------------------- + * Return the disk geometry for a particular disk + * Input: + * Disk *disk - Disk geometry + * kdev_t dev - Device node + * int *geom - Returns geometry fields + * geom[0] = heads + * geom[1] = sectors + * geom[2] = cylinders + *-------------------------------------------------------------*/ +int megaraid_biosparam(Disk *disk, kdev_t dev, int *geom) +{ + int heads, sectors, cylinders; + mega_host_config *megaCfg; + + /* Get pointer to host config structure */ + megaCfg = (mega_host_config *)disk->device->host->hostdata; + + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + /* Handle extended translation size for logical drives > 1Gb */ + if (disk->capacity >= 0x200000) { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); + } + + /* return result */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return 0; +} + +#ifdef MODULE +Scsi_Host_Template driver_template = MEGARAID; + +#include "scsi_module.c" +#endif diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/megaraid.h linux/drivers/scsi/megaraid.h --- v2.0.35/linux/drivers/scsi/megaraid.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/megaraid.h Sun Nov 15 10:33:09 1998 @@ -0,0 +1,282 @@ +#ifndef __MEGARAID_H__ +#define __MEGARAID_H__ + +#define IN_ISR 0x80000000L +#define NO_INTR 0x40000000L +#define IN_TIMEOUT 0x20000000L +#define PENDING 0x10000000L +#define BOARD_QUARTZ 0x08000000L + +#define SCB_ACTIVE 0x1 +#define SCB_WAITQ 0x2 +#define SCB_ISSUED 0x4 + +#define SCB_FREE -1 +#define SCB_RESET -2 +#define SCB_ABORT -3 +#define SCB_LOCKED -4 + +#define MEGA_CMD_TIMEOUT 10 + +#define MAX_SGLIST 20 +#define MAX_COMMANDS 254 + +#define MAX_LOGICAL_DRIVES 8 +#define MAX_CHANNEL 5 +#define MAX_TARGET 15 +#define MAX_PHYSICAL_DRIVES MAX_CHANNEL*MAX_TARGET + +#define INQUIRY_DATA_SIZE 0x24 +#define MAX_CDB_LEN 0x0A +#define MAX_REQ_SENSE_LEN 0x20 + +#define INTR_VALID 0x40 + +/* Mailbox commands */ +#define MEGA_MBOXCMD_LREAD 0x01 +#define MEGA_MBOXCMD_LWRITE 0x02 +#define MEGA_MBOXCMD_PASSTHRU 0x03 +#define MEGA_MBOXCMD_ADAPTERINQ 0x05 + +/* Offsets into Mailbox */ +#define COMMAND_PORT 0x00 +#define COMMAND_ID_PORT 0x01 +#define SG_LIST_PORT0 0x08 +#define SG_LIST_PORT1 0x09 +#define SG_LIST_PORT2 0x0a +#define SG_LIST_PORT3 0x0b +#define SG_ELEMENT_PORT 0x0d +#define NO_FIRED_PORT 0x0f + +/* I/O Port offsets */ +#define I_CMD_PORT 0x00 +#define I_ACK_PORT 0x00 +#define I_TOGGLE_PORT 0x01 +#define INTR_PORT 0x0a + +#define MAILBOX_SIZE 70 +#define MBOX_BUSY_PORT 0x00 +#define MBOX_PORT0 0x04 +#define MBOX_PORT1 0x05 +#define MBOX_PORT2 0x06 +#define MBOX_PORT3 0x07 +#define ENABLE_MBOX_REGION 0x0B + +/* I/O Port Values */ +#define ISSUE_BYTE 0x10 +#define ACK_BYTE 0x08 +#define ENABLE_INTR_BYTE 0xc0 +#define DISABLE_INTR_BYTE 0x00 +#define VALID_INTR_BYTE 0x40 +#define MBOX_BUSY_BYTE 0x10 +#define ENABLE_MBOX_BYTE 0x00 + +/* Setup some port macros here */ +#define WRITE_MAILBOX(base,offset,value) *(base+offset)=value +#define READ_MAILBOX(base,offset) *(base+offset) + +#define WRITE_PORT(base,offset,value) outb_p(value,base+offset) +#define READ_PORT(base,offset) inb_p(base+offset) + +#define ISSUE_COMMAND(base) WRITE_PORT(base,I_CMD_PORT,ISSUE_BYTE) +#define CLEAR_INTR(base) WRITE_PORT(base,I_ACK_PORT,ACK_BYTE) +#define ENABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE) +#define DISABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE) + +/* Define AMI's PCI codes */ +#undef PCI_VENDOR_ID_AMI +#undef PCI_DEVICE_ID_AMI_MEGARAID + +#ifndef PCI_VENDOR_ID_AMI +#define PCI_VENDOR_ID_AMI 0x101E +#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 +#endif + +#define PCI_CONF_BASE_ADDR_OFFSET 0x10 +#define PCI_CONF_IRQ_OFFSET 0x3c + +#if LINUX_VERSION_CODE < 0x20100 +#define MEGARAID \ + { NULL, /* Next */\ + NULL, /* Usage Count Pointer */\ + NULL, /* /proc Directory Entry */\ + megaraid_proc_info, /* /proc Info Function */\ + "MegaRAID", /* Driver Name */\ + megaraid_detect, /* Detect Host Adapter */\ + megaraid_release, /* Release Host Adapter */\ + megaraid_info, /* Driver Info Function */\ + megaraid_command, /* Command Function */\ + megaraid_queue, /* Queue Command Function */\ + megaraid_abort, /* Abort Command Function */\ + megaraid_reset, /* Reset Command Function */\ + NULL, /* Slave Attach Function */\ + megaraid_biosparam, /* Disk BIOS Parameters */\ + 1, /* # of cmds that can be\ + outstanding at any time */\ + 7, /* HBA Target ID */\ + MAX_SGLIST, /* Scatter/Gather Table Size */\ + 1, /* SCSI Commands per LUN */\ + 0, /* Present */\ + 0, /* Default Unchecked ISA DMA */\ + ENABLE_CLUSTERING } /* Enable Clustering */ +#else +#define MEGARAID \ + {\ + name: "MegaRAID", /* Driver Name */\ + proc_info: megaraid_proc_info, /* /proc driver info */\ + detect: megaraid_detect, /* Detect Host Adapter */\ + release: megaraid_release, /* Release Host Adapter */\ + info: megaraid_info, /* Driver Info Function */\ + command: megaraid_command, /* Command Function */\ + queuecommand: megaraid_queue, /* Queue Command Function */\ + abort: megaraid_abort, /* Abort Command Function */\ + reset: megaraid_reset, /* Reset Command Function */\ + bios_param: megaraid_biosparam, /* Disk BIOS Parameters */\ + can_queue: 255, /* Can Queue */\ + this_id: 7, /* HBA Target ID */\ + sg_tablesize: MAX_SGLIST, /* Scatter/Gather Table Size */\ + cmd_per_lun: 1, /* SCSI Commands per LUN */\ + present: 0, /* Present */\ + unchecked_isa_dma:0, /* Default Unchecked ISA DMA */\ + use_clustering: ENABLE_CLUSTERING /* Enable Clustering */\ + } +#endif + +/* 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]; +} 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]; +} mega_LDRV_INFO; + +typedef struct _mega_PDRV_INFO +{ + u_char PDrvState[MAX_PHYSICAL_DRIVES]; + u_char resvd; +} mega_PDRV_INFO; + +// RAID inquiry: Mailbox command 0x5 +typedef struct _mega_RAIDINQ +{ + mega_ADP_INFO AdpInfo; + mega_LDRV_INFO LogdrvInfo; + mega_PDRV_INFO PhysdrvInfo; +} mega_RAIDINQ; + +// 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; +} 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]; +} mega_mailbox; + +typedef struct _mega_sglist +{ + u_long address; + u_long length; +} mega_sglist; + +/* Queued command data */ +typedef struct _mega_scb mega_scb; + +struct _mega_scb +{ + int idx; + u_long flag; + Scsi_Cmnd *SCpnt; + u_char mboxData[16]; + mega_passthru pthru; + mega_sglist *sgList; + mega_scb *next; +}; + +/* Per-controller data */ +typedef struct _mega_host_config +{ + u_char numldrv; + u_long flag; + u_long base; + + struct tq_struct megaTq; + + /* Host adapter parameters */ + u_char fwVer[7]; + u_char 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]; + + u_char max_cmds; + mega_scb scbList[MAX_COMMANDS]; +} mega_host_config; + +extern struct proc_dir_entry proc_scsi_megaraid; + +const char *megaraid_info( struct Scsi_Host * ); +int megaraid_detect( Scsi_Host_Template * ); +int megaraid_release(struct Scsi_Host *); +int megaraid_command( Scsi_Cmnd * ); +int megaraid_abort( Scsi_Cmnd * ); +int megaraid_reset( Scsi_Cmnd *, unsigned int); +int megaraid_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) ); +int megaraid_biosparam( Disk *, kdev_t, int * ); +int megaraid_proc_info( char *buffer, char **start, off_t offset, + int length, int hostno, int inout ); + +#endif diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c --- v2.0.35/linux/drivers/scsi/ncr53c8xx.c Sun Nov 15 10:49:45 1998 +++ linux/drivers/scsi/ncr53c8xx.c Sun Nov 15 10:33:09 1998 @@ -7655,7 +7655,7 @@ ** field of the controller's struct ncb. ** ** Possible cases: hs sir msg_in value send goto -** We try try to negotiate: +** We try to negotiate: ** -> target doesnt't msgin NEG FAIL noop defa. - dispatch ** -> target rejected our msg NEG FAIL reject defa. - dispatch ** -> target answered (ok) NEG SYNC sdtr set - clrack diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h --- v2.0.35/linux/drivers/scsi/ppa.h Sun Nov 15 10:49:45 1998 +++ linux/drivers/scsi/ppa.h Sun Nov 15 10:33:09 1998 @@ -7,6 +7,7 @@ * All comments to David. */ +#include /* CONFIG_SCSI_PPA_HAVE_PEDANTIC */ #ifndef _PPA_H #define _PPA_H @@ -54,12 +55,12 @@ * * Fixed all problems in the parport sharing scheme. Now ppa can be safe * used with lp or other parport devices on the same parallel port. - * 1997 by Andrea Arcangeli + * 1997 by Andrea Arcangeli * [1.39] * * Little fix in ppa engine to ensure that ppa don' t release parport * or disconnect in wrong cases. - * 1997 by Andrea Arcangeli + * 1997 by Andrea Arcangeli * [1.40] * * Corrected ppa.h for 2.1.x kernels (>=2.1.85) diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v2.0.35/linux/drivers/scsi/scsi.c Sun Nov 15 10:49:45 1998 +++ linux/drivers/scsi/scsi.c Sun Nov 15 10:33:10 1998 @@ -284,6 +284,7 @@ {"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN}, {"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN}, +{"NAKAMICH","MJ-5.16S","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, @@ -687,6 +688,7 @@ case TYPE_MOD: case TYPE_PROCESSOR: case TYPE_SCANNER: + case TYPE_MEDIUM_CHANGER: SDpnt->writeable = 1; break; case TYPE_WORM: @@ -750,6 +752,13 @@ SDpnt->borken = 0; /* + * If we want to only allow I/O to one of the luns attached to this device + * at a time, then we set this flag. + */ + if (bflags & BLIST_SINGLELUN) + SDpnt->single_lun = 1; + + /* * These devices need this "key" to unlock the devices so we can use it */ if ((bflags & BLIST_KEY) != 0) { @@ -790,13 +799,6 @@ */ if (bflags & BLIST_NOLUN) return 0; /* break; */ - - /* - * If we want to only allow I/O to one of the luns attached to this device - * at a time, then we set this flag. - */ - if (bflags & BLIST_SINGLELUN) - SDpnt->single_lun = 1; /* * If this device is known to support sparse multiple units, override the diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c --- v2.0.35/linux/drivers/scsi/scsicam.c Thu Nov 7 01:25:21 1996 +++ linux/drivers/scsi/scsicam.c Sun Nov 15 10:33:10 1998 @@ -50,6 +50,7 @@ struct buffer_head *bh; int ret_code; int size = disk->capacity; + unsigned long temp_cyl; if (!(bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024))) return -1; @@ -72,6 +73,11 @@ if (ret_code || ip[0] > 255 || ip[1] > 63) { ip[0] = 64; ip[1] = 32; + temp_cyl = size / (ip[0] * ip[1]); + if (temp_cyl > 65534) { + ip[0] = 255; + ip[1] = 63; + } ip[2] = size / (ip[0] * ip[1]); } @@ -118,6 +124,8 @@ end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2); end_head = largest->end_head; end_sector = largest->end_sector & 0x3f; + + if( end_head + 1 == 0 || end_sector == 0 ) return -1; #ifdef DEBUG printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n", diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- v2.0.35/linux/drivers/scsi/sd.c Sun Nov 15 10:49:45 1998 +++ linux/drivers/scsi/sd.c Sun Nov 15 10:33:10 1998 @@ -424,8 +424,8 @@ /* If we had an ILLEGAL REQUEST returned, then we may have * performed an unsupported command. The only thing this should be * would be a ten byte read where only a six byte read was supported. - * Also, on a system where READ CAPACITY failed, we have have read - * past the end of the disk. + * Also, on a system where READ CAPACITY failed, we have read past + * the end of the disk. */ if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) { diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c --- v2.0.35/linux/drivers/scsi/seagate.c Mon May 27 03:37:18 1996 +++ linux/drivers/scsi/seagate.c Sun Nov 15 10:33:10 1998 @@ -689,7 +689,7 @@ return DID_BAD_TARGET; /* - * We work it differently depending on if this is is "the first time," + * We work it differently depending on if this is "the first time," * or a reconnect. If this is a reselect phase, then SEL will * be asserted, and we must skip selection / arbitration phases. */ diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c --- v2.0.35/linux/drivers/scsi/u14-34f.c Mon Jul 13 13:46:36 1998 +++ linux/drivers/scsi/u14-34f.c Sun Nov 15 10:33:10 1998 @@ -1,6 +1,18 @@ /* * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters. * + * 26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111 + * Added command line option (et:[y|n]) to use the existing + * translation (returned by scsicam_bios_param) as disk geometry. + * The default is et:n, which uses the disk geometry jumpered + * on the board. + * The default value et:n is compatible with all previous revisions + * of this driver. + * + * 28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104 + * Increased busy timeout from 10 msec. to 200 msec. while + * processing interrupts. + * * 18 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102 * Improved abort handling during the eh recovery process. * @@ -9,7 +21,7 @@ * abort and reset routines. * Added command line options (eh:[y|n]) to choose between * new_eh_code and the old scsi code. - * If linux verion >= 2.1.101 the default is eh:y, while the eh + * If linux version >= 2.1.101 the default is eh:y, while the eh * option is ignored for previous releases and the old scsi code * is used. * @@ -241,20 +253,23 @@ * * eh:y use new scsi code (linux 2.2 only); * eh:n use old scsi code; + * et:y use disk geometry returned by scsicam_bios_param; + * et:n use disk geometry jumpered on the board; * lc:y enables linked commands; * lc:n disables linked commands; * of:y enables old firmware support; * of:n disables old firmware support; * mq:xx set the max queue depth to the value xx (2 <= xx <= 8). * - * The default value is: "u14-34f=lc:n,of:n,mq:8". An example using the list - * of detection probes could be: "u14-34f=0x230,0x340,lc:y,of:n,mq:4,eh:n". + * The default value is: "u14-34f=lc:n,of:n,mq:8,et:n". + * An example using the list of detection probes could be: + * "u14-34f=0x230,0x340,lc:y,of:n,mq:4,eh:n,et:n". * * When loading as a module, parameters can be specified as well. * The above example would be (use 1 in place of y and 0 in place of n): * * modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \ - * max_queue_depth=4 use_new_eh_code=0 + * max_queue_depth=4 use_new_eh_code=0 ext_tran=0 * * ---------------------------------------------------------------------------- * In this implementation, linked commands are designed to work with any DISK @@ -310,6 +325,7 @@ MODULE_PARM(link_statistics, "i"); MODULE_PARM(max_queue_depth, "i"); MODULE_PARM(use_new_eh_code, "i"); +MODULE_PARM(ext_tran, "i"); MODULE_AUTHOR("Dario Ballabio"); #endif @@ -401,6 +417,7 @@ #undef DEBUG_RESET #undef DEBUG_GENERATE_ERRORS #undef DEBUG_GENERATE_ABORTS +#undef DEBUG_GEOMETRY #define MAX_ISA 3 #define MAX_VESA 1 @@ -552,6 +569,7 @@ static int do_trace = FALSE; static int setup_done = FALSE; static int link_statistics = 0; +static int ext_tran = FALSE; #if defined(HAVE_OLD_UX4F_FIRMWARE) static int have_old_firmware = TRUE; @@ -872,9 +890,9 @@ if (j == 0) { printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n"); - printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c.\n", + printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c, et:%c.\n", driver_name, YESNO(have_old_firmware), YESNO(linked_comm), - max_queue_depth, YESNO(use_new_eh_code)); + max_queue_depth, YESNO(use_new_eh_code), YESNO(ext_tran)); } printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d.\n", @@ -918,6 +936,7 @@ else if (!strncmp(cur, "mq:", 3)) max_queue_depth = val; else if (!strncmp(cur, "ls:", 3)) link_statistics = val; else if (!strncmp(cur, "eh:", 3)) use_new_eh_code = val; + else if (!strncmp(cur, "et:", 3)) ext_tran = val; if ((cur = strchr(cur, ','))) ++cur; } @@ -1551,6 +1570,18 @@ dkinfo[0] = HD(j)->heads; dkinfo[1] = HD(j)->sectors; dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors); + + if (ext_tran && (scsicam_bios_param(disk, dev, dkinfo) < 0)) { + dkinfo[0] = 255; + dkinfo[1] = 63; + dkinfo[2] = size / (dkinfo[0] * dkinfo[1]); + } + +#if defined (DEBUG_GEOMETRY) + printk ("%s: biosparam, head=%d, sec=%d, cyl=%d.\n", driver_name, + dkinfo[0], dkinfo[1], dkinfo[2]); +#endif + return FALSE; } @@ -1733,7 +1764,7 @@ HD(j)->iocount); /* Check if this board is still busy */ - if (wait_on_busy(sh[j]->io_port, MAXLOOP)) { + if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) { outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR); printk("%s: ihdlr, busy timeout error, irq %d, reg 0x%x, count %d.\n", BN(j), irq, reg, HD(j)->iocount); diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h --- v2.0.35/linux/drivers/scsi/u14-34f.h Mon Jul 13 13:46:36 1998 +++ linux/drivers/scsi/u14-34f.h Sun Nov 15 10:33:10 1998 @@ -4,6 +4,7 @@ #ifndef _U14_34F_H #define _U14_34F_H +#include #include int u14_34f_detect(Scsi_Host_Template *); @@ -15,7 +16,7 @@ int u14_34f_old_reset(Scsi_Cmnd *, unsigned int); int u14_34f_biosparam(Disk *, kdev_t, int *); -#define U14_34F_VERSION "4.31.00" +#define U14_34F_VERSION "4.33.00" #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c --- v2.0.35/linux/drivers/scsi/wd7000.c Sun Nov 15 10:49:45 1998 +++ linux/drivers/scsi/wd7000.c Sun Nov 15 10:33:10 1998 @@ -102,44 +102,56 @@ * * Revisions by Miroslav Zagorac * - * 08/24/1996. + * -- 08/24/1996. -------------------------------------------------------------- + * Enhancement for wd7000_detect function has been made, so you don't have + * to enter BIOS ROM address in initialisation data (see struct Config). + * We cannot detect IRQ, DMA and I/O base address for now, so we have to + * enter them as arguments while wd_7000 is detected. If someone has IRQ, + * DMA or an I/O base address set to some other value, he can enter them in + * a configuration without any problem. + * Also I wrote a function wd7000_setup, so now you can enter WD-7000 + * definition as kernel arguments, as in lilo.conf: + * + * append="wd7000=IRQ,DMA,IO" + * + * PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize + * adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is + * useless for Linux. B^) + * + * -- 09/06/1996. -------------------------------------------------------------- + * Auto detecting of an I/O base address from wd7000_detect function is + * removed, some little bugs too... + * + * Thanks to Roger Scott for driver debugging. + * + * -- 06/07/1997. -------------------------------------------------------------- + * Added support for /proc file system (/proc/scsi/wd7000/[0...] files). + * Now, the driver can handle hard disks with capacity >1GB. + * + * -- 01/15/1998. -------------------------------------------------------------- + * Added support for BUS_ON and BUS_OFF parameters in config line. + * Miscellaneous cleanups. Syntax of the append line is changed to: + * + * append="wd7000=IRQ,DMA,IO[,BUS_ON[,BUS_OFF]]" + * + * , where BUS_ON and BUS_OFF are time in nanoseconds. + * + * -- 03/01/1998. -------------------------------------------------------------- + * The WD7000 driver now works on kernels' >= 2.1.x + * + * -- 06/11/1998. -------------------------------------------------------------- + * Ugly init_scbs, alloc_scbs and free_scb functions are changed with + * scbs_init, scb_alloc and scb_free. Now, source code is identical on + * 2.0.xx and 2.1.xx kernels. + * WD7000 specific definitions are moved from this file to wd7000.h. * - * Enhancement for wd7000_detect function has been made, so you don't have - * to enter BIOS ROM adress in initialisation data (see struct Config). - * We cannot detect IRQ, DMA and I/O base address for now, so we have to - * enter them as arguments while wd_7000 is detected. If someone has IRQ, - * DMA or I/O base address set to some other value, he can enter them in - * configuration without any problem. Also I wrote a function wd7000_setup, - * so now you can enter WD-7000 definition as kernel arguments, - * as in lilo.conf: - * - * append="wd7000=IRQ,DMA,IO" - * - * PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize - * adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is - * useless for Linux. B^) - * - * - * 09/06/1996. - * - * Autodetecting of I/O base address from wd7000_detect function is removed, - * some little bugs removed, etc... - * - * Thanks to Roger Scott for driver debugging. - * - * 06/07/1997 - * - * Added support for /proc file system (/proc/scsi/wd7000/[0...] files). - * Now, driver can handle hard disks with capacity >1GB. - * - * 01/15/1998 - * - * Added support for BUS_ON and BUS_OFF parameters in config line. - * Miscellaneous cleanup. */ - #ifdef MODULE -#include +# include +#endif + +#if (LINUX_VERSION_CODE >= 0x020100) +# include #endif #include @@ -155,16 +167,17 @@ #include #include #include +#include +#include #include "scsi.h" #include "hosts.h" #include "sd.h" #include -#define ANY2SCSI_INLINE /* undef this to use old macros */ -#undef WD7000_DEBUG /* general debug */ +#undef WD7000_DEBUG /* general debug */ +#define WD7000_DEFINES /* This must be defined! */ #include "wd7000.h" -#include struct proc_dir_entry proc_scsi_wd7000 = @@ -176,86 +189,28 @@ 2 }; - -/* - * Mailbox structure sizes. - * I prefer to keep the number of ICMBs much larger than the number of - * OGMBs. OGMBs are used very quickly by the driver to start one or - * more commands, while ICMBs are used by the host adapter per command. - */ -#define OGMB_CNT 16 -#define ICMB_CNT 32 - -/* - * Scb's are shared by all active adapters. So, if they all become busy, - * callers may be made to wait in alloc_scbs for them to free. That can - * be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q. If you'd - * rather conserve memory, use a smaller number (> 0, of course) - things - * will should still work OK. - */ -#define MAX_SCBS 32 - -/* - * WD7000-specific mailbox structure - * - */ -typedef volatile struct mailbox { - unchar status; - unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ -} Mailbox; - -/* - * This structure should contain all per-adapter global data. I.e., any - * new global per-adapter data should put in here. - */ -typedef struct adapter { - struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ - int iobase; /* This adapter's I/O base address */ - int irq; /* This adapter's IRQ level */ - int dma; /* This adapter's DMA channel */ - int int_counter; /* This adapter's interrupt counter */ - int bus_on; /* This adapter's BUS_ON time */ - int bus_off; /* This adapter's BUS_OFF time */ - struct { /* This adapter's mailboxes */ - Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ - Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ - } mb; - int next_ogmb; /* to reduce contention at mailboxes */ - unchar control; /* shadows CONTROL port value */ - unchar rev1, rev2; /* filled in by wd7000_revision */ -} Adapter; - /* * (linear) base address for ROM BIOS */ -static const long wd7000_biosaddr[] = -{ +static const long wd7000_biosaddr[] = { 0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000, 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000 }; -#define NUM_ADDRS (sizeof(wd7000_biosaddr)/sizeof(long)) +#define NUM_ADDRS (sizeof (wd7000_biosaddr) / sizeof (long)) -static const unsigned short wd7000_iobase[] = -{ +static const ushort wd7000_iobase[] = { 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8, 0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8 }; -#define NUM_IOPORTS (sizeof(wd7000_iobase)/sizeof(unsigned short)) +#define NUM_IOPORTS (sizeof (wd7000_iobase) / sizeof (ushort)) static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 }; -#define NUM_IRQS (sizeof(wd7000_irq)/sizeof(short)) +#define NUM_IRQS (sizeof (wd7000_irq) / sizeof (short)) static const short wd7000_dma[] = { 5, 6, 7 }; -#define NUM_DMAS (sizeof(wd7000_dma)/sizeof(short)) - -/* - * possible irq range - */ -#define IRQ_MIN 3 -#define IRQ_MAX 15 -#define IRQS (IRQ_MAX - IRQ_MIN + 1) +#define NUM_DMAS (sizeof (wd7000_dma) / sizeof (short)) /* * The following is set up by wd7000_detect, and used thereafter by @@ -265,23 +220,6 @@ */ static struct Scsi_Host *wd7000_host[IRQS]; -#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ -#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ - -/* - * Standard Adapter Configurations - used by wd7000_detect - */ -typedef struct { - short irq; /* IRQ level */ - short dma; /* DMA channel */ - unsigned iobase; /* I/O base address */ - short bus_on; /* Time that WD7000 spends on the AT-bus when */ - /* transferring data. BIOS default is 8000ns. */ - short bus_off; /* Time that WD7000 spends OFF THE BUS after */ - /* while it is transferring data. */ - /* BIOS default is 1875ns */ -} Config; - /* * Add here your configuration... */ @@ -294,276 +232,12 @@ }; #define NUM_CONFIGS (sizeof(configs)/sizeof(Config)) -/* - * The following list defines strings to look for in the BIOS that identify - * it as the WD7000-FASST2 SST BIOS. I suspect that something should be - * added for the Future Domain version. - */ -typedef struct signature { - const void *sig; /* String to look for */ - unsigned ofs; /* offset from BIOS base address */ - unsigned len; /* length of string */ -} Signature; - static const Signature signatures[] = { {"SSTBIOS", 0x0000d, 7} /* "SSTBIOS" @ offset 0x0000d */ }; #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature)) - -/* - * I/O Port Offsets and Bit Definitions - * 4 addresses are used. Those not defined here are reserved. - */ -#define ASC_STAT 0 /* Status, Read */ -#define ASC_COMMAND 0 /* Command, Write */ -#define ASC_INTR_STAT 1 /* Interrupt Status, Read */ -#define ASC_INTR_ACK 1 /* Acknowledge, Write */ -#define ASC_CONTROL 2 /* Control, Write */ - -/* - * ASC Status Port - */ -#define INT_IM 0x80 /* Interrupt Image Flag */ -#define CMD_RDY 0x40 /* Command Port Ready */ -#define CMD_REJ 0x20 /* Command Port Byte Rejected */ -#define ASC_INIT 0x10 /* ASC Initialized Flag */ -#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */ - -/* - * COMMAND opcodes - * - * Unfortunately, I have no idea how to properly use some of these commands, - * as the OEM manual does not make it clear. I have not been able to use - * enable/disable unsolicited interrupts or the reset commands with any - * discernible effect whatsoever. I think they may be related to certain - * ICB commands, but again, the OEM manual doesn't make that clear. - */ -#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ -#define INITIALIZATION 1 /* initialization (10 bytes) */ -#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ -#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ -#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ -#define SOFT_RESET 5 /* SCSI bus soft reset */ -#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ -#define START_OGMB 0x80 /* start command in OGMB (n) */ -#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ - /* where (n) = lower 6 bits */ -/* - * For INITIALIZATION: - */ -typedef struct initCmd { - unchar op; /* command opcode (= 1) */ - unchar ID; /* Adapter's SCSI ID */ - unchar bus_on; /* Bus on time, x 125ns (see below) */ - unchar bus_off; /* Bus off time, "" "" */ - unchar rsvd; /* Reserved */ - unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ - unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ - unchar icmbs; /* Number of incoming MBs, "" "" */ -} InitCmd; - -/* - * Interrupt Status Port - also returns diagnostic codes at ASC reset - * - * if msb is zero, the lower bits are diagnostic status - * Diagnostics: - * 01 No diagnostic error occurred - * 02 RAM failure - * 03 FIFO R/W failed - * 04 SBIC register read/write failed - * 05 Initialization D-FF failed - * 06 Host IRQ D-FF failed - * 07 ROM checksum error - * Interrupt status (bitwise): - * 10NNNNNN outgoing mailbox NNNNNN is free - * 11NNNNNN incoming mailbox NNNNNN needs service - */ -#define MB_INTR 0xC0 /* Mailbox Service possible/required */ -#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */ -#define MB_MASK 0x3f /* mask for mailbox number */ - -/* - * CONTROL port bits - */ -#define INT_EN 0x08 /* Interrupt Enable */ -#define DMA_EN 0x04 /* DMA Enable */ -#define SCSI_RES 0x02 /* SCSI Reset */ -#define ASC_RES 0x01 /* ASC Reset */ - -/* - * Driver data structures: - * - mb and scbs are required for interfacing with the host adapter. - * An SCB has extra fields not visible to the adapter; mb's - * _cannot_ do this, since the adapter assumes they are contiguous in - * memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact - * to access them. - * - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each; - * the additional bytes are used only by the driver. - * - For now, a pool of SCBs are kept in global storage by this driver, - * and are allocated and freed as needed. - * - * The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command, - * not when it has finished. Since the SCB must be around for completion, - * problems arise when SCBs correspond to OGMBs, which may be reallocated - * earlier (or delayed unnecessarily until a command completes). - * Mailboxes are used as transient data structures, simply for - * carrying SCB addresses to/from the 7000-FASST2. - * - * Note also since SCBs are not "permanently" associated with mailboxes, - * there is no need to keep a global list of Scsi_Cmnd pointers indexed - * by OGMB. Again, SCBs reference their Scsi_Cmnds directly, so mailbox - * indices need not be involved. - */ - -/* - * WD7000-specific scatter/gather element structure - */ -typedef struct sgb { - unchar len[3]; - unchar ptr[3]; /* Also SCSI-style - MSB first */ -} Sgb; - -typedef struct scb { /* Command Control Block 5.4.1 */ - unchar op; /* Command Control Block Operation Code */ - unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ - /* Outbound data transfer, length is checked */ - /* Inbound data transfer, length is checked */ - /* Logical Unit Number */ - unchar cdb[12]; /* SCSI Command Block */ - volatile unchar status; /* SCSI Return Status */ - volatile unchar vue; /* Vendor Unique Error Code */ - unchar maxlen[3]; /* Maximum Data Transfer Length */ - unchar dataptr[3]; /* SCSI Data Block Pointer */ - unchar linkptr[3]; /* Next Command Link Pointer */ - unchar direc; /* Transfer Direction */ - unchar reserved2[6]; /* SCSI Command Descriptor Block */ - /* end of hardware SCB */ - Scsi_Cmnd *SCpnt; /* Scsi_Cmnd using this SCB */ - Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */ - Adapter *host; /* host adapter */ - struct scb *next; /* for lists of scbs */ -} Scb; - -/* - * This driver is written to allow host-only commands to be executed. - * These use a 16-byte block called an ICB. The format is extended by the - * driver to 18 bytes, to support the status returned in the ICMB and - * an execution phase code. - * - * There are other formats besides these; these are the ones I've tried - * to use. Formats for some of the defined ICB opcodes are not defined - * (notably, get/set unsolicited interrupt status) in my copy of the OEM - * manual, and others are ambiguous/hard to follow. - */ -#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */ -#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */ -#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */ -#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */ -#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */ -#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */ -#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */ - /* 0x87 is reserved */ -#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */ -#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */ -#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */ -#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */ -#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */ -#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */ -#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */ -#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */ - -typedef struct icbRecvCmd { - unchar op; - unchar IDlun; /* Initiator SCSI ID/lun */ - unchar len[3]; /* command buffer length */ - unchar ptr[3]; /* command buffer address */ - unchar rsvd[7]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbRecvCmd; - -typedef struct icbSendStat { - unchar op; - unchar IDlun; /* Target SCSI ID/lun */ - unchar stat; /* (outgoing) completion status byte 1 */ - unchar rsvd[12]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbSendStat; - -typedef struct icbRevLvl { - unchar op; - volatile unchar primary; /* primary revision level (returned) */ - volatile unchar secondary; /* secondary revision level (returned) */ - unchar rsvd[12]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbRevLvl; - -typedef struct icbUnsMask { /* I'm totally guessing here */ - unchar op; - volatile unchar mask[14]; /* mask bits */ -#if 0 - unchar rsvd[12]; /* reserved */ -#endif - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbUnsMask; - -typedef struct icbDiag { - unchar op; - unchar type; /* diagnostics type code (0-3) */ - unchar len[3]; /* buffer length */ - unchar ptr[3]; /* buffer address */ - unchar rsvd[7]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbDiag; - -#define ICB_DIAG_POWERUP 0 /* Power-up diags only */ -#define ICB_DIAG_WALKING 1 /* walking 1's pattern */ -#define ICB_DIAG_DMA 2 /* DMA - system memory diags */ -#define ICB_DIAG_FULL 3 /* do both 1 & 2 */ - -typedef struct icbParms { - unchar op; - unchar rsvd1; /* reserved */ - unchar len[3]; /* parms buffer length */ - unchar ptr[3]; /* parms buffer address */ - unchar idx[2]; /* index (MSB-LSB) */ - unchar rsvd2[5]; /* reserved */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbParms; - -typedef struct icbAny { - unchar op; - unchar data[14]; /* format-specific data */ - volatile unchar vue; /* vendor-unique error code */ - volatile unchar status; /* returned (icmb) status */ - volatile unchar phase; /* used by interrupt handler */ -} IcbAny; - -typedef union icb { - unchar op; /* ICB opcode */ - IcbRecvCmd recv_cmd; /* format for receive command */ - IcbSendStat send_stat; /* format for send status */ - IcbRevLvl rev_lvl; /* format for get revision level */ - IcbDiag diag; /* format for execute diagnostics */ - IcbParms eparms; /* format for get/set exec parms */ - IcbAny icb; /* generic format */ - unchar data[18]; -} Icb; - - /* * Driver SCB structure pool. * @@ -571,8 +245,7 @@ * structure is not part of the Adapter structure. */ static Scb scbs[MAX_SCBS]; -static Scb *scbfree = NULL; /* free list */ -static int freescbs = MAX_SCBS; /* free list counter */ + /* * END of data/declarations - code follows. @@ -599,6 +272,7 @@ * * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values * are 8000ns for BUS_ON and 1875ns for BUS_OFF. + * * eg: * wd7000=7,6,0x350 * @@ -611,14 +285,15 @@ short i, j; if (wd7000_card_num >= NUM_CONFIGS) { - printk ("wd7000_setup: Too many \"wd7000=\" configurations in " - "command line!\n"); + printk ("%s: Too many \"wd7000=\" configurations in " + "command line!\n", __FUNCTION__); return; } if ((ints[0] < 3) || (ints[0] > 5)) - printk ("wd7000_setup: Error in command line! " - "Usage: wd7000=,,IO>[,[,]]\n"); + printk ("%s: Error in command line! " + "Usage: wd7000=,,[,[,]]\n", + __FUNCTION__); else { for (i = 0; i < NUM_IRQS; i++) if (ints[1] == wd7000_irq[i]) @@ -660,7 +335,7 @@ configs[wd7000_card_num].bus_on = BUS_ON; } else - configs[wd7000_card_num].bus_on = ints[4] / 125.0; + configs[wd7000_card_num].bus_on = ints[4] / 125; } else configs[wd7000_card_num].bus_on = BUS_ON; @@ -672,12 +347,12 @@ configs[wd7000_card_num].bus_off = BUS_OFF; } else - configs[wd7000_card_num].bus_off = ints[5] / 125.0; + configs[wd7000_card_num].bus_off = ints[5] / 125; } else configs[wd7000_card_num].bus_off = BUS_OFF; - if (wd7000_card_num) + if (wd7000_card_num) { for (i = 0; i < (wd7000_card_num - 1); i++) for (j = i + 1; j < wd7000_card_num; j++) if (configs[i].irq == configs[j].irq) { @@ -692,9 +367,11 @@ setup_error ("duplicated I/O base address!", ints); return; } + } #ifdef WD7000_DEBUG - printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n", + printk ("%s: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n", + __FUNCTION__, configs[wd7000_card_num].irq, configs[wd7000_card_num].dma, configs[wd7000_card_num].iobase, @@ -707,7 +384,6 @@ } -#ifdef ANY2SCSI_INLINE /* * Since they're used a lot, I've redone the following from the macros * formerly in wd7000.h, hopefully to speed them up by getting rid of @@ -716,45 +392,25 @@ * xany2scsi and xscsi2int were not being used, and are no longer defined. * (They were simply 4-byte versions of these routines). */ -typedef union { /* let's cheat... */ - int i; - unchar u[sizeof (int)]; /* the sizeof(int) makes it more portable */ -} i_u; - - -static inline void any2scsi (unchar * scsi, int any) +static inline void any2scsi (unchar *scsi, int any) { *scsi++ = ((i_u) any).u[2]; *scsi++ = ((i_u) any).u[1]; - *scsi++ = ((i_u) any).u[0]; + *scsi = ((i_u) any).u[0]; } -static inline int scsi2int (unchar * scsi) +static inline int scsi2int (unchar *scsi) { i_u result; result.i = 0; /* clears unused bytes */ - *(result.u + 2) = *scsi++; - *(result.u + 1) = *scsi++; - *(result.u) = *scsi++; + result.u[2] = *scsi++; + result.u[1] = *scsi++; + result.u[0] = *scsi; return (result.i); } -#else -/* - * These are the old ones - I've just moved them here... - */ -#undef any2scsi -#define any2scsi(up, p) (up)[0] = (((unsigned long) (p)) >> 16); \ - (up)[1] = ((unsigned long) (p)) >> 8; \ - (up)[2] = ((unsigned long) (p)); - -#undef scsi2int -#define scsi2int(up) ( (((unsigned long) *(up)) << 16) + \ - (((unsigned long) (up)[1]) << 8) + \ - ((unsigned long) (up)[2]) ) -#endif static inline void wd7000_enable_intr (Adapter *host) @@ -773,12 +429,10 @@ } -#define WAITnexttimeout 200 /* 2 seconds */ - -static inline short WAIT (unsigned port, unsigned mask, unsigned allof, unsigned noneof) +static inline short WAIT (uint port, uint mask, uint allof, uint noneof) { - register unsigned WAITbits; - register unsigned long WAITtimeout = jiffies + WAITnexttimeout; + register uint WAITbits; + register ulong WAITtimeout = jiffies + WAITnexttimeout; while (jiffies <= WAITtimeout) { WAITbits = inb (port) & mask; @@ -791,131 +445,97 @@ } -static inline void delay (unsigned how_long) +static inline void delay (uint how_long) { - register unsigned long time = jiffies + how_long; + register ulong time = jiffies + how_long; while (jiffies < time); } -static inline int command_out (Adapter * host, unchar * cmd, int len) +static inline int wd7000_command_out (Adapter *host, unchar *cmd, int len) { - if (!WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - while (len--) { + if (! WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { + for ( ; len--; cmd++) do { outb (*cmd, host->iobase + ASC_COMMAND); WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0); } while (inb (host->iobase + ASC_STAT) & CMD_REJ); - cmd++; - } - return (1); } - printk ("wd7000 command_out: WAIT failed(%d)\n", len + 1); + printk ("%s: WAIT failed (%d)\n", __FUNCTION__, len + 1); return (0); } -/* - * This version of alloc_scbs is in preparation for supporting multiple - * commands per lun and command chaining, by queueing pending commands. - * We will need to allocate Scbs in blocks since they will wait to be - * executed so there is the possibility of deadlock otherwise. - * Also, to keep larger requests from being starved by smaller requests, - * we limit access to this routine with an internal busy flag, so that - * the satisfiability of a request is not dependent on the size of the - * request. - */ -static inline Scb *alloc_scbs (int needed) +static inline void scbs_init (void) { - register Scb *scb, *p; - register unsigned long flags; - register unsigned long timeout = jiffies + WAITnexttimeout; - register unsigned long now; - static int busy = 0; - int i; - - if (needed <= 0) - return (NULL); /* sanity check */ - - save_flags (flags); - cli (); - while (busy) { /* someone else is allocating */ - sti (); /* Yes this is really needed here */ - for (now = jiffies; now == jiffies; ); /* wait a jiffy */ - cli (); - } - busy = 1; /* not busy now; it's our turn */ - - while (freescbs < needed) { - timeout = jiffies + WAITnexttimeout; - do { - sti (); /* Yes this is really needed here */ - for (now = jiffies; now == jiffies; ); /* wait a jiffy */ - cli (); - } while (freescbs < needed && jiffies <= timeout); - /* - * If we get here with enough free Scbs, we can take them. - * Otherwise, we timed out and didn't get enough. - */ - if (freescbs < needed) { - busy = 0; - panic ("wd7000: can't get enough free SCBs.\n"); - restore_flags (flags); - return (NULL); - } - } - scb = scbfree; - freescbs -= needed; - for (i = 0; i < needed; i++) { - p = scbfree; - scbfree = p->next; - } - p->next = NULL; - busy = 0; /* we're done */ - - restore_flags (flags); + short i; - return (scb); + for (i = 0; i < MAX_SCBS; i++) + memset ((void *) &(scbs[i]), 0, sizeof (Scb)); } -static inline void free_scb (Scb *scb) +static inline Scb *scb_alloc (void) { - register unsigned long flags; + Scb *scb = NULL; + ulong flags; + short i; +#ifdef WD7000_DEBUG + short free_scbs = 0; +#endif save_flags (flags); cli (); - memset (scb, 0, sizeof (Scb)); - scb->next = scbfree; - scbfree = scb; - freescbs++; + for (i = 0; i < MAX_SCBS; i++) + if (! scbs[i].used) { + scbs[i].used = 1; + scb = &(scbs[i]); + + break; + } + +#ifdef WD7000_DEBUG + for (i = 0; i < MAX_SCBS; i++) + free_scbs += scbs[i].used ? 0 : 1; + + printk ("wd7000_%s: allocating scb (0x%08x), %d scbs free\n", + __FUNCTION__, (int) scb, free_scbs); +#endif restore_flags (flags); + + return (scb); } -static inline void init_scbs (void) +static inline void scb_free (Scb *scb) { - int i; - unsigned long flags; + short i; + ulong flags; save_flags (flags); cli (); - scbfree = &(scbs[0]); - memset (scbs, 0, sizeof (scbs)); - for (i = 0; i < MAX_SCBS - 1; i++) { - scbs[i].next = &(scbs[i + 1]); - scbs[i].SCpnt = NULL; - } - scbs[MAX_SCBS - 1].next = NULL; - scbs[MAX_SCBS - 1].SCpnt = NULL; + for (i = 0; i < MAX_SCBS; i++) + if (&(scbs[i]) == scb) { + memset ((void *) &(scbs[i]), 0, sizeof (Scb)); + + break; + } + + if (i == MAX_SCBS) + printk ("wd7000_%s: trying to free alien scb (0x%08x)...\n", + __FUNCTION__, (int) scb); +#ifdef WD7000_DEBUG + else + printk ("wd7000_%s: freeing scb (0x%08x)\n", __FUNCTION__, (int) scb); +#endif restore_flags (flags); } @@ -927,18 +547,19 @@ */ { register int i, ogmb; - register unsigned long flags; + ulong flags; unchar start_ogmb; Mailbox *ogmbs = host->mb.ogmb; int *next_ogmb = &(host->next_ogmb); #ifdef WD7000_DEBUG - printk ("wd7000 mail_out: 0x%06lx", (long) scbptr); + printk ("wd7000_%s: 0x%08x", __FUNCTION__, (int) scbptr); #endif /* We first look for a free outgoing mailbox */ save_flags (flags); cli (); + ogmb = *next_ogmb; for (i = 0; i < OGMB_CNT; i++) { if (ogmbs[ogmb].status == 0) { @@ -954,10 +575,11 @@ else ogmb = (++ogmb) % OGMB_CNT; } + restore_flags (flags); #ifdef WD7000_DEBUG - printk (", scb is 0x%06lx", (long) scbptr); + printk (", scb is 0x%08x", (int) scbptr); #endif if (i >= OGMB_CNT) { @@ -978,7 +600,7 @@ wd7000_enable_intr (host); start_ogmb = START_OGMB | ogmb; - command_out (host, &start_ogmb, 1); + wd7000_command_out (host, &start_ogmb, 1); #ifdef WD7000_DEBUG printk (", awaiting interrupt.\n"); @@ -988,7 +610,7 @@ } -int make_code (unsigned hosterr, unsigned scsierr) +int make_code (uint hosterr, uint scsierr) { #ifdef WD7000_DEBUG int in_error = hosterr; @@ -998,40 +620,51 @@ case 0: /* Reserved */ hosterr = DID_ERROR; break; + case 1: /* Command Complete, no errors */ hosterr = DID_OK; break; + case 2: /* Command complete, error logged in scb status (scsierr) */ hosterr = DID_OK; break; + case 4: /* Command failed to complete - timeout */ hosterr = DID_TIME_OUT; break; + case 5: /* Command terminated; Bus reset by external device */ hosterr = DID_RESET; break; + case 6: /* Unexpected Command Received w/ host as target */ hosterr = DID_BAD_TARGET; break; + case 80: /* Unexpected Reselection */ case 81: /* Unexpected Selection */ hosterr = DID_BAD_INTR; break; + case 82: /* Abort Command Message */ hosterr = DID_ABORT; break; + case 83: /* SCSI Bus Software Reset */ case 84: /* SCSI Bus Hardware Reset */ hosterr = DID_RESET; break; + default: /* Reserved */ hosterr = DID_ERROR; } + #ifdef WD7000_DEBUG if (scsierr || hosterr) printk ("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", scsierr, in_error, hosterr); #endif + return (scsierr | (hosterr << 16)); } @@ -1039,14 +672,18 @@ static void wd7000_scsi_done (Scsi_Cmnd *SCpnt) { #ifdef WD7000_DEBUG - printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt); + printk ("%s: 0x%08x\n", __FUNCTION__, (int) SCpnt); #endif SCpnt->SCp.phase = 0; } -#define wd7000_intr_ack(host) outb (0, host->iobase + ASC_INTR_ACK) +static inline void wd7000_intr_ack (Adapter *host) +{ + outb (0, host->iobase + ASC_INTR_ACK); +} + void wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs) { @@ -1061,16 +698,16 @@ host->int_counter++; #ifdef WD7000_DEBUG - printk ("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host); + printk ("%s: irq = %d, host = 0x%08x\n", __FUNCTION__, irq, (int) host); #endif flag = inb (host->iobase + ASC_INTR_STAT); #ifdef WD7000_DEBUG - printk ("wd7000_intr_handle: intr stat = 0x%02x\n", flag); + printk ("%s: intr stat = 0x%02x\n", __FUNCTION__, flag); #endif - if (!(inb (host->iobase + ASC_STAT) & INT_IM)) { + if (! (inb (host->iobase + ASC_STAT) & INT_IM)) { /* NB: these are _very_ possible if IRQ 15 is being used, since * it's the "garbage collector" on the 2nd 8259 PIC. Specifically, * any interrupt signal into the 8259 which can't be identified @@ -1080,7 +717,7 @@ * problems would be indistinguishable from valid interrupts... */ #ifdef WD7000_DEBUG - printk ("wd7000_intr_handle: phantom interrupt...\n"); + printk ("%s: phantom interrupt...\n", __FUNCTION__); #endif wd7000_intr_ack (host); return; @@ -1088,9 +725,9 @@ if (flag & MB_INTR) { /* The interrupt is for a mailbox */ - if (!(flag & IMB_INTR)) { + if (! (flag & IMB_INTR)) { #ifdef WD7000_DEBUG - printk ("wd7000_intr_handle: free outgoing mailbox\n"); + printk ("%s: free outgoing mailbox\n", __FUNCTION__); #endif /* * If sleep_on() and the "interrupt on free OGMB" command are @@ -1104,15 +741,19 @@ /* The interrupt is for an incoming mailbox */ icmb = flag & MB_MASK; icmb_status = icmbs[icmb].status; + if (icmb_status & 0x80) { /* unsolicited - result in ICMB */ #ifdef WD7000_DEBUG - printk ("wd7000_intr_handle: unsolicited interrupt 0x%02xh\n", - icmb_status); + printk ("%s: unsolicited interrupt 0x%02x\n", + __FUNCTION__, icmb_status); #endif wd7000_intr_ack (host); return; } - scb = (struct scb *) scsi2int ((unchar *) icmbs[icmb].scbptr); + + /* Aaaargh! (Zaga) */ + scb = (Scb *) bus_to_virt (scsi2int ((unchar *) icmbs[icmb].scbptr)); + icmbs[icmb].status = 0; if (!(scb->op & ICB_OP_MASK)) { /* an SCB is done */ SCpnt = scb->SCpnt; @@ -1122,7 +763,7 @@ errstatus = make_code (host_error, scsi_error); SCpnt->result = errstatus; - free_scb (scb); + scb_free (scb); SCpnt->scsi_done (SCpnt); } @@ -1136,6 +777,26 @@ } wd7000_intr_ack (host); + +#ifdef WD7000_DEBUG + printk ("%s: return from interrupt handler\n", __FUNCTION__); +#endif +} + + +void do_wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs) +{ +#if (LINUX_VERSION_CODE >= 0x020100) + ulong flags; + + spin_lock_irqsave (&io_request_lock, flags); +#endif + + wd7000_intr_handle (irq, dev_id, regs); + +#if (LINUX_VERSION_CODE >= 0x020100) + spin_unlock_irqrestore (&io_request_lock, flags); +#endif } @@ -1143,33 +804,31 @@ { register Scb *scb; register Sgb *sgb; - register unchar *cdb = (unchar *) SCpnt->cmnd; - register unchar idlun; - register short cdblen; - Adapter *host = (Adapter *) SCpnt->host->hostdata; + register Adapter *host = (Adapter *) SCpnt->host->hostdata; + + if ((scb = scb_alloc ()) == NULL) { + printk ("%s: Cannot allocate SCB!\n", __FUNCTION__); + return (0); + } - cdblen = SCpnt->cmd_len; - idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); SCpnt->scsi_done = done; SCpnt->SCp.phase = 1; - scb = alloc_scbs (1); - scb->idlun = idlun; - memcpy (scb->cdb, cdb, cdblen); + SCpnt->host_scribble = (unchar *) scb; + scb->idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); scb->direc = 0x40; /* Disable direction check */ - scb->SCpnt = SCpnt; /* so we can find stuff later */ - SCpnt->host_scribble = (unchar *) scb; scb->host = host; + memcpy (scb->cdb, SCpnt->cmnd, SCpnt->cmd_len); if (SCpnt->use_sg) { struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; - unsigned i; + uint i; - if (SCpnt->host->sg_tablesize == SG_NONE) { - panic ("wd7000_queuecommand: scatter/gather not supported.\n"); - } + if (SCpnt->host->sg_tablesize == SG_NONE) + panic ("%s: scatter/gather not supported.\n", __FUNCTION__); #ifdef WD7000_DEBUG - printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg); + else + printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg); #endif sgb = scb->sgb; @@ -1188,7 +847,7 @@ any2scsi (scb->maxlen, SCpnt->request_bufflen); } - while (!mail_out (host, scb)); /* keep trying */ + while (! mail_out (host, scb)); /* keep trying */ return (1); } @@ -1196,7 +855,8 @@ int wd7000_command (Scsi_Cmnd *SCpnt) { - wd7000_queuecommand (SCpnt, wd7000_scsi_done); + if (! wd7000_queuecommand (SCpnt, wd7000_scsi_done)) + return (-1); while (SCpnt->SCp.phase > 0) barrier (); /* phase counts scbs down to 0 */ @@ -1207,32 +867,35 @@ int wd7000_diagnostics (Adapter *host, int code) { - static IcbDiag icb = - {ICB_OP_DIAGNOSTICS}; + static IcbDiag icb = { ICB_OP_DIAGNOSTICS }; static unchar buf[256]; - unsigned long timeout; + ulong timeout; + /* + * This routine is only called at init, so there should be OGMBs + * available. I'm assuming so here. If this is going to + * fail, I can just let the timeout catch the failure. + */ icb.type = code; any2scsi (icb.len, sizeof (buf)); any2scsi (icb.ptr, (int) &buf); icb.phase = 1; + + mail_out (host, (Scb *) &icb); + /* - * This routine is only called at init, so there should be OGMBs - * available. I'm assuming so here. If this is going to - * fail, I can just let the timeout catch the failure. + * Wait up to 2 seconds for completion. */ - mail_out (host, (struct scb *) &icb); - timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */ - while (icb.phase && jiffies < timeout) - barrier (); /* wait for completion */ + for (timeout = jiffies + WAITnexttimeout; icb.phase && (jiffies < timeout); ) + barrier (); if (icb.phase) { - printk ("wd7000_diagnostics: timed out.\n"); + printk ("%s: timed out.\n", __FUNCTION__); return (0); } + if (make_code (icb.vue | (icb.status << 8), 0)) { - printk ("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", - icb.vue, icb.status); + printk ("%s: failed (0x%02x,0x%02x)\n", __FUNCTION__, icb.vue, icb.status); return (0); } @@ -1265,28 +928,35 @@ host->control = 0; /* this must always shadow ASC_CONTROL */ if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { - printk ("wd7000_init: WAIT timed out.\n"); + printk ("%s: WAIT timed out.\n", __FUNCTION__); return (0); /* 0 = not ok */ } if ((diag = inb (host->iobase + ASC_INTR_STAT)) != 1) { - printk ("wd7000_init: "); + printk ("%s: ", __FUNCTION__); switch (diag) { case 2: printk ("RAM failure.\n"); break; + case 3: printk ("FIFO R/W failed\n"); break; + case 4: printk ("SBIC register R/W failed\n"); break; + case 5: printk ("Initialization D-FF failed.\n"); break; + case 6: printk ("Host IRQ D-FF failed.\n"); break; + case 7: printk ("ROM checksum error.\n"); break; + default: printk ("diagnostic code 0x%02Xh received.\n", diag); } + return (0); } @@ -1294,30 +964,33 @@ memset (&(host->mb), 0, sizeof (host->mb)); /* Execute init command */ - any2scsi ((unchar *) & (init_cmd.mailboxes), (int) &(host->mb)); - if (!command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) { - printk ("wd7000_init: adapter initialization failed.\n"); + any2scsi ((unchar *) &(init_cmd.mailboxes), (int) &(host->mb)); + + if (! wd7000_command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) { + printk ("%s: adapter initialization failed.\n", __FUNCTION__); return (0); } if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) { - printk ("wd7000_init: WAIT timed out.\n"); + printk ("%s: WAIT timed out.\n", __FUNCTION__); return (0); } - if (request_irq (host->irq, wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) { - printk ("wd7000_init: can't get IRQ %d.\n", host->irq); + if (request_irq (host->irq, do_wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) { + printk ("%s: can't get IRQ %d.\n", __FUNCTION__, host->irq); return (0); } + if (request_dma (host->dma, "wd7000")) { - printk ("wd7000_init: can't get DMA channel %d.\n", host->dma); + printk ("%s: can't get DMA channel %d.\n", __FUNCTION__, host->dma); free_irq (host->irq, NULL); return (0); } + wd7000_enable_dma (host); wd7000_enable_intr (host); - if (!wd7000_diagnostics (host, ICB_DIAG_FULL)) { + if (! wd7000_diagnostics (host, ICB_DIAG_FULL)) { free_dma (host->dma); free_irq (host->irq, NULL); return (0); @@ -1329,30 +1002,31 @@ void wd7000_revision (Adapter *host) { - static IcbRevLvl icb = - {ICB_OP_GET_REVISION}; + static IcbRevLvl icb = { ICB_OP_GET_REVISION }; - icb.phase = 1; /* * Like diagnostics, this is only done at init time, in fact, from * wd7000_detect, so there should be OGMBs available. If it fails, * the only damage will be that the revision will show up as 0.0, * which in turn means that scatter/gather will be disabled. */ - mail_out (host, (struct scb *) &icb); + icb.phase = 1; + mail_out (host, (Scb *) &icb); + while (icb.phase) barrier (); /* wait for completion */ + host->rev1 = icb.primary; host->rev2 = icb.secondary; } #undef SPRINTF -#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } +#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host) { - unsigned long flags; + ulong flags; save_flags (flags); cli (); @@ -1375,9 +1049,9 @@ int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout) { struct Scsi_Host *host = NULL; - Scsi_Device *scd = scsi_devices; + Scsi_Device *scd; Adapter *adapter; - unsigned long flags; + ulong flags; char *pos = buffer; short i; @@ -1399,7 +1073,7 @@ /* * Host not found! */ - if (!host) + if (! host) return (-ESRCH); /* @@ -1455,9 +1129,15 @@ /* * Display driver information for each device attached to the board. */ +#if (LINUX_VERSION_CODE >= 0x020100) + scd = host->host_queue; +#else + scd = scsi_devices; +#endif + SPRINTF ("\nAttached devices: %s\n", scd ? "" : "none"); - for (; scd; scd = scd->next) + for ( ; scd; scd = scd->next) if (scd->host->host_no == hostno) { SPRINTF (" [Channel: %02d, Id: %02d, Lun: %02d] ", scd->channel, scd->id, scd->lun); @@ -1505,50 +1185,73 @@ { short present = 0, biosaddr_ptr, sig_ptr, i, pass; short biosptr[NUM_CONFIGS]; - unsigned iobase; + uint iobase; Adapter *host = NULL; struct Scsi_Host *sh; - for (i = 0; i < IRQS; wd7000_host[i++] = NULL) ; - for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1) ; - - tpnt->proc_dir = &proc_scsi_wd7000; - tpnt->proc_info = &wd7000_proc_info; +#ifdef WD7000_DEBUG + printk ("%s: started\n", __FUNCTION__); +#endif /* * Set up SCB free list, which is shared by all adapters */ - init_scbs (); + scbs_init (); + + for (i = 0; i < IRQS; wd7000_host[i++] = NULL); + for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1); + + tpnt->proc_dir = &proc_scsi_wd7000; + tpnt->proc_info = &wd7000_proc_info; for (pass = 0; pass < NUM_CONFIGS; pass++) { + short bios_match = 1; + +#ifdef WD7000_DEBUG + printk ("%s: pass %d\n", __FUNCTION__, pass + 1); +#endif + /* * First, search for BIOS SIGNATURE... */ - for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++) - for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) { + for (biosaddr_ptr = 0; bios_match && (biosaddr_ptr < NUM_ADDRS); biosaddr_ptr++) + for (sig_ptr = 0; bios_match && (sig_ptr < NUM_SIGNATURES); sig_ptr++) { for (i = 0; i < pass; i++) if (biosptr[i] == biosaddr_ptr) break; - if ((i == pass) && - !memcmp ((void *) (wd7000_biosaddr[biosaddr_ptr] + - signatures[sig_ptr].ofs), signatures[sig_ptr].sig, - signatures[sig_ptr].len)) - goto bios_matched; - } + if (i == pass) { +#if (LINUX_VERSION_CODE >= 0x020100) + char *biosaddr = (char *) ioremap (wd7000_biosaddr[biosaddr_ptr] + + signatures[sig_ptr].ofs, + signatures[sig_ptr].len); +#else + char *biosaddr = (char *) (wd7000_biosaddr[biosaddr_ptr] + + signatures[sig_ptr].ofs); +#endif + bios_match = memcmp (biosaddr, signatures[sig_ptr].sig, + signatures[sig_ptr].len); - bios_matched: - /* - * BIOS SIGNATURE has been found. - */ +#if (LINUX_VERSION_CODE >= 0x020100) + iounmap (biosaddr); +#else +#endif + if (! bios_match) { + /* + * BIOS SIGNATURE has been found. + */ + biosptr[pass] = biosaddr_ptr; #ifdef WD7000_DEBUG - printk ("wd7000_detect: pass %d\n", pass + 1); + printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n", + wd7000_biosaddr[biosaddr_ptr]); +#endif + } + } + } - if (biosaddr_ptr == NUM_ADDRS) +#ifdef WD7000_DEBUG + if (bios_match) printk ("WD-7000 SST BIOS not detected...\n"); - else - printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n", - wd7000_biosaddr[biosaddr_ptr]); #endif if (configs[pass].irq < 0) @@ -1557,13 +1260,12 @@ iobase = configs[pass].iobase; #ifdef WD7000_DEBUG - printk ("wd7000_detect: check IO 0x%x region...\n", iobase); + printk ("%s: check IO 0x%x region...\n", __FUNCTION__, iobase); #endif - if (!check_region (iobase, 4)) { - + if (! check_region (iobase, 4)) { #ifdef WD7000_DEBUG - printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase); + printk ("%s: ASC reset (IO 0x%x) ...", __FUNCTION__, iobase); #endif /* * ASC reset... @@ -1596,7 +1298,7 @@ host = (Adapter *) sh->hostdata; #ifdef WD7000_DEBUG - printk ("wd7000_detect: adapter allocated at 0x%x\n", (int) host); + printk ("%s: adapter allocated at 0x%x\n", __FUNCTION__, (int) host); #endif memset (host, 0, sizeof (Adapter)); @@ -1610,14 +1312,12 @@ host->sh = wd7000_host[host->irq - IRQ_MIN] = sh; #ifdef WD7000_DEBUG - printk ("wd7000_detect: Trying init WD-7000 card at IO " - "0x%x, IRQ %d, DMA %d...\n", - host->iobase, host->irq, host->dma); + printk ("%s: Trying to init WD-7000 card at IO 0x%x, IRQ %d, DMA %d...\n", + __FUNCTION__, host->iobase, host->irq, host->dma); #endif - if (!wd7000_init (host)) { /* Initialization failed */ + if (! wd7000_init (host)) { /* Initialization failed */ scsi_unregister (sh); - continue; } @@ -1639,9 +1339,6 @@ present++; /* count it */ - if (biosaddr_ptr != NUM_ADDRS) - biosptr[pass] = biosaddr_ptr; - printk ("Western Digital WD-7000 (rev %d.%d) ", host->rev1, host->rev2); printk ("using IO 0x%x, IRQ %d, DMA %d.\n", @@ -1653,12 +1350,12 @@ #ifdef WD7000_DEBUG else - printk ("wd7000_detect: IO 0x%x region already allocated!\n", iobase); + printk ("%s: IO 0x%x region is already allocated!\n", __FUNCTION__, iobase); #endif } - if (!present) + if (! present) printk ("Failed initialization of WD-7000 SCSI card!\n"); return (present); @@ -1673,7 +1370,7 @@ Adapter *host = (Adapter *) SCpnt->host->hostdata; if (inb (host->iobase + ASC_STAT) & INT_IM) { - printk ("wd7000_abort: lost interrupt\n"); + printk ("%s: lost interrupt\n", __FUNCTION__); wd7000_intr_handle (host->irq, NULL, NULL); return (SCSI_ABORT_SUCCESS); @@ -1686,7 +1383,7 @@ /* * I also have no idea how to do a reset... */ -int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int flags) +int wd7000_reset (Scsi_Cmnd *SCpnt, uint flags) { return (SCSI_RESET_PUNT); } @@ -1698,7 +1395,7 @@ int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip) { #ifdef WD7000_DEBUG - printk ("wd7000_biosparam: dev=%s, size=%d, ", kdevname (dev), disk->capacity); + printk ("%s: dev=%s, size=%d, ", __FUNCTION__, kdevname (dev), disk->capacity); #endif /* @@ -1720,8 +1417,9 @@ if ((scsicam_bios_param (disk, dev, info) < 0) || !(((info[0] == 64) && (info[1] == 32)) || ((info[0] == 255) && (info[1] == 63)))) { - printk ("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n" - " using extended translation.\n"); + printk ("%s: unable to verify geometry for disk with >1GB.\n" + " using extended translation.\n", + __FUNCTION__); ip[0] = 255; ip[1] = 63; @@ -1733,7 +1431,8 @@ ip[2] = info[2]; if (info[0] == 255) - printk ("wd7000_biosparam: current partition table is using extended translation.\n"); + printk ("%s: current partition table is using extended translation.\n", + __FUNCTION__); } } diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h --- v2.0.35/linux/drivers/scsi/wd7000.h Sun Nov 15 10:49:45 1998 +++ linux/drivers/scsi/wd7000.h Sun Nov 15 10:33:10 1998 @@ -1,5 +1,3 @@ -#ifndef _WD7000_H - /* $Id: $ * * Header file for the WD-7000 driver for Linux @@ -10,19 +8,12 @@ * * Revision by Miroslav Zagorac Jun 1997. */ +#ifndef _WD7000_H +#include #include #include -int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host); -int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout); -int wd7000_detect (Scsi_Host_Template *); -int wd7000_command (Scsi_Cmnd *); -int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -int wd7000_abort (Scsi_Cmnd *); -int wd7000_reset (Scsi_Cmnd *, unsigned int); -int wd7000_biosparam (Disk *, kdev_t, int *); - #ifndef NULL #define NULL 0L #endif @@ -41,18 +32,415 @@ #define WD7000_Q 16 #define WD7000_SG 16 -#define WD7000 { NULL, NULL, \ - NULL, \ - NULL, \ - "Western Digital WD-7000", \ - wd7000_detect, \ - NULL, \ - NULL, \ - wd7000_command, \ - wd7000_queuecommand, \ - wd7000_abort, \ - wd7000_reset, \ - NULL, \ - wd7000_biosparam, \ - WD7000_Q, 7, WD7000_SG, 1, 0, 1, ENABLE_CLUSTERING} +#ifdef WD7000_DEFINES +/* + * Mailbox structure sizes. + * I prefer to keep the number of ICMBs much larger than the number of + * OGMBs. OGMBs are used very quickly by the driver to start one or + * more commands, while ICMBs are used by the host adapter per command. + */ +#define OGMB_CNT 16 +#define ICMB_CNT 32 + +/* + * Scb's are shared by all active adapters. If you'd rather conserve + * memory, use a smaller number (> 0, of course) - things will should + * still work OK. + */ +#define MAX_SCBS (4 * WD7000_Q) + +/* + * WD7000-specific mailbox structure + */ +typedef volatile struct { + unchar status; + unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ +} Mailbox; + +/* + * This structure should contain all per-adapter global data. I.e., any + * new global per-adapter data should put in here. + */ +typedef struct { + struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ + int iobase; /* This adapter's I/O base address */ + int irq; /* This adapter's IRQ level */ + int dma; /* This adapter's DMA channel */ + int int_counter; /* This adapter's interrupt counter */ + int bus_on; /* This adapter's BUS_ON time */ + int bus_off; /* This adapter's BUS_OFF time */ + struct { /* This adapter's mailboxes */ + Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ + Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ + } mb; + int next_ogmb; /* to reduce contention at mailboxes */ + unchar control; /* shadows CONTROL port value */ + unchar rev1; /* filled in by wd7000_revision */ + unchar rev2; +} Adapter; + + +/* + * possible irq range + */ +#define IRQ_MIN 3 +#define IRQ_MAX 15 +#define IRQS (IRQ_MAX - IRQ_MIN + 1) + +#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ +#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ + +/* + * Standard Adapter Configurations - used by wd7000_detect + */ +typedef struct { + short irq; /* IRQ level */ + short dma; /* DMA channel */ + uint iobase; /* I/O base address */ + short bus_on; /* Time that WD7000 spends on the AT-bus when */ + /* transferring data. BIOS default is 8000ns. */ + short bus_off; /* Time that WD7000 spends OFF THE BUS after */ + /* while it is transferring data. */ + /* BIOS default is 1875ns */ +} Config; + + +/* + * The following list defines strings to look for in the BIOS that identify + * it as the WD7000-FASST2 SST BIOS. I suspect that something should be + * added for the Future Domain version. + */ +typedef struct { + const char *sig; /* String to look for */ + ulong ofs; /* offset from BIOS base address */ + uint len; /* length of string */ +} Signature; + +/* + * I/O Port Offsets and Bit Definitions + * 4 addresses are used. Those not defined here are reserved. + */ +#define ASC_STAT 0 /* Status, Read */ +#define ASC_COMMAND 0 /* Command, Write */ +#define ASC_INTR_STAT 1 /* Interrupt Status, Read */ +#define ASC_INTR_ACK 1 /* Acknowledge, Write */ +#define ASC_CONTROL 2 /* Control, Write */ + +/* + * ASC Status Port + */ +#define INT_IM 0x80 /* Interrupt Image Flag */ +#define CMD_RDY 0x40 /* Command Port Ready */ +#define CMD_REJ 0x20 /* Command Port Byte Rejected */ +#define ASC_INIT 0x10 /* ASC Initialized Flag */ +#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */ + +/* + * COMMAND opcodes + * + * Unfortunately, I have no idea how to properly use some of these commands, + * as the OEM manual does not make it clear. I have not been able to use + * enable/disable unsolicited interrupts or the reset commands with any + * discernible effect whatsoever. I think they may be related to certain + * ICB commands, but again, the OEM manual doesn't make that clear. + */ +#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ +#define INITIALIZATION 1 /* initialization (10 bytes) */ +#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ +#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ +#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ +#define SOFT_RESET 5 /* SCSI bus soft reset */ +#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ +#define START_OGMB 0x80 /* start command in OGMB (n) */ +#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ + /* where (n) = lower 6 bits */ +/* + * For INITIALIZATION: + */ +typedef struct { + unchar op; /* command opcode (= 1) */ + unchar ID; /* Adapter's SCSI ID */ + unchar bus_on; /* Bus on time, x 125ns (see below) */ + unchar bus_off; /* Bus off time, "" "" */ + unchar rsvd; /* Reserved */ + unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ + unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ + unchar icmbs; /* Number of incoming MBs, "" "" */ +} InitCmd; + +/* + * Interrupt Status Port - also returns diagnostic codes at ASC reset + * + * if msb is zero, the lower bits are diagnostic status + * Diagnostics: + * 01 No diagnostic error occurred + * 02 RAM failure + * 03 FIFO R/W failed + * 04 SBIC register read/write failed + * 05 Initialization D-FF failed + * 06 Host IRQ D-FF failed + * 07 ROM checksum error + * Interrupt status (bitwise): + * 10NNNNNN outgoing mailbox NNNNNN is free + * 11NNNNNN incoming mailbox NNNNNN needs service + */ +#define MB_INTR 0xC0 /* Mailbox Service possible/required */ +#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */ +#define MB_MASK 0x3f /* mask for mailbox number */ + +/* + * CONTROL port bits + */ +#define INT_EN 0x08 /* Interrupt Enable */ +#define DMA_EN 0x04 /* DMA Enable */ +#define SCSI_RES 0x02 /* SCSI Reset */ +#define ASC_RES 0x01 /* ASC Reset */ + +/* + * Driver data structures: + * - mb and scbs are required for interfacing with the host adapter. + * An SCB has extra fields not visible to the adapter; mb's + * _cannot_ do this, since the adapter assumes they are contiguous in + * memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact + * to access them. + * - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each; + * the additional bytes are used only by the driver. + * - For now, a pool of SCBs are kept in global storage by this driver, + * and are allocated and freed as needed. + * + * The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command, + * not when it has finished. Since the SCB must be around for completion, + * problems arise when SCBs correspond to OGMBs, which may be reallocated + * earlier (or delayed unnecessarily until a command completes). + * Mailboxes are used as transient data structures, simply for + * carrying SCB addresses to/from the 7000-FASST2. + * + * Note also since SCBs are not "permanently" associated with mailboxes, + * there is no need to keep a global list of Scsi_Cmnd pointers indexed + * by OGMB. Again, SCBs reference their Scsi_Cmnds directly, so mailbox + * indices need not be involved. + */ + +/* + * WD7000-specific scatter/gather element structure + */ +typedef struct { + unchar len[3]; + unchar ptr[3]; /* Also SCSI-style - MSB first */ +} Sgb; + +typedef struct { /* Command Control Block 5.4.1 */ + unchar op; /* Command Control Block Operation Code */ + unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ + /* Outbound data transfer, length is checked */ + /* Inbound data transfer, length is checked */ + /* Logical Unit Number */ + unchar cdb[12]; /* SCSI Command Block */ + volatile unchar status; /* SCSI Return Status */ + volatile unchar vue; /* Vendor Unique Error Code */ + unchar maxlen[3]; /* Maximum Data Transfer Length */ + unchar dataptr[3]; /* SCSI Data Block Pointer */ + unchar linkptr[3]; /* Next Command Link Pointer */ + unchar direc; /* Transfer Direction */ + unchar reserved2[6]; /* SCSI Command Descriptor Block */ + /* end of hardware SCB */ + Scsi_Cmnd *SCpnt; /* Scsi_Cmnd using this SCB */ + Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */ + Adapter *host; /* host adapter */ + unchar used; /* flag */ +} Scb; + +/* + * This driver is written to allow host-only commands to be executed. + * These use a 16-byte block called an ICB. The format is extended by the + * driver to 18 bytes, to support the status returned in the ICMB and + * an execution phase code. + * + * There are other formats besides these; these are the ones I've tried + * to use. Formats for some of the defined ICB opcodes are not defined + * (notably, get/set unsolicited interrupt status) in my copy of the OEM + * manual, and others are ambiguous/hard to follow. + */ +#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */ +#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */ +#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */ +#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */ +#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */ +#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */ +#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */ + /* 0x87 is reserved */ +#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */ +#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */ +#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */ +#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */ +#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */ +#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */ +#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */ +#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */ + +typedef struct { + unchar op; + unchar IDlun; /* Initiator SCSI ID/lun */ + unchar len[3]; /* command buffer length */ + unchar ptr[3]; /* command buffer address */ + unchar rsvd[7]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbRecvCmd; + +typedef struct { + unchar op; + unchar IDlun; /* Target SCSI ID/lun */ + unchar stat; /* (outgoing) completion status byte 1 */ + unchar rsvd[12]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbSendStat; + +typedef struct { + unchar op; + volatile unchar primary; /* primary revision level (returned) */ + volatile unchar secondary; /* secondary revision level (returned) */ + unchar rsvd[12]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbRevLvl; + +typedef struct { /* I'm totally guessing here */ + unchar op; + volatile unchar mask[14]; /* mask bits */ +#if 0 + unchar rsvd[12]; /* reserved */ #endif + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbUnsMask; + +typedef struct { + unchar op; + unchar type; /* diagnostics type code (0-3) */ + unchar len[3]; /* buffer length */ + unchar ptr[3]; /* buffer address */ + unchar rsvd[7]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbDiag; + +#define ICB_DIAG_POWERUP 0 /* Power-up diags only */ +#define ICB_DIAG_WALKING 1 /* walking 1's pattern */ +#define ICB_DIAG_DMA 2 /* DMA - system memory diags */ +#define ICB_DIAG_FULL 3 /* do both 1 & 2 */ + +typedef struct { + unchar op; + unchar rsvd1; /* reserved */ + unchar len[3]; /* parms buffer length */ + unchar ptr[3]; /* parms buffer address */ + unchar idx[2]; /* index (MSB-LSB) */ + unchar rsvd2[5]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbParms; + +typedef struct { + unchar op; + unchar data[14]; /* format-specific data */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbAny; + +typedef union { + unchar op; /* ICB opcode */ + IcbRecvCmd recv_cmd; /* format for receive command */ + IcbSendStat send_stat; /* format for send status */ + IcbRevLvl rev_lvl; /* format for get revision level */ + IcbDiag diag; /* format for execute diagnostics */ + IcbParms eparms; /* format for get/set exec parms */ + IcbAny icb; /* generic format */ + unchar data[18]; +} Icb; + +#define WAITnexttimeout 200 /* 2 seconds */ + +typedef union { /* let's cheat... */ + int i; + unchar u[sizeof (int)]; /* the sizeof(int) makes it more portable */ +} i_u; + +#endif /* WD7000_DEFINES */ + + +#if (LINUX_VERSION_CODE >= 0x020100) + +#define WD7000 { \ + proc_dir: &proc_scsi_wd7000, \ + proc_info: wd7000_proc_info, \ + name: "Western Digital WD-7000", \ + detect: wd7000_detect, \ + command: wd7000_command, \ + queuecommand: wd7000_queuecommand, \ + abort: wd7000_abort, \ + reset: wd7000_reset, \ + bios_param: wd7000_biosparam, \ + can_queue: WD7000_Q, \ + this_id: 7, \ + sg_tablesize: WD7000_SG, \ + cmd_per_lun: 1, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 0 \ +} + +#else /* Use old scsi code */ + +#define WD7000 { \ + proc_dir: &proc_scsi_wd7000, \ + proc_info: wd7000_proc_info, \ + name: "Western Digital WD-7000", \ + detect: wd7000_detect, \ + command: wd7000_command, \ + queuecommand: wd7000_queuecommand, \ + abort: wd7000_abort, \ + reset: wd7000_reset, \ + bios_param: wd7000_biosparam, \ + can_queue: WD7000_Q, \ + this_id: 7, \ + sg_tablesize: WD7000_SG, \ + cmd_per_lun: 1, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING, \ +} + +#endif /* LINUX_VERSION_CODE */ + + +extern struct proc_dir_entry proc_scsi_wd7000; + + +#ifdef WD7000_DEFINES +int wd7000_diagnostics (Adapter *, int); +int wd7000_init (Adapter *); +void wd7000_revision (Adapter *); +#endif /* WD7000_DEFINES */ + +void wd7000_setup (char *, int *); +int make_code (uint, uint); +void wd7000_intr_handle (int, void *, struct pt_regs *); +void do_wd7000_intr_handle (int, void *, struct pt_regs *); +int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int wd7000_command (Scsi_Cmnd *); +int wd7000_set_info (char *, int, struct Scsi_Host *); +int wd7000_proc_info (char *, char **, off_t, int, int, int); +int wd7000_detect (Scsi_Host_Template *); +int wd7000_abort (Scsi_Cmnd *); +int wd7000_reset (Scsi_Cmnd *, uint); +int wd7000_biosparam (Disk *, kdev_t, int *); + +#endif /* _WD7000_H */ diff -u --recursive --new-file v2.0.35/linux/drivers/sound/Makefile linux/drivers/sound/Makefile --- v2.0.35/linux/drivers/sound/Makefile Sun Jun 30 01:43:41 1996 +++ linux/drivers/sound/Makefile Sun Nov 15 10:33:11 1998 @@ -146,11 +146,10 @@ $(LD) -r -o sound.o $(FIXEDOBJS) sound.a modules: local.h sound.o - ln -fs `pwd`/sound.o $(TOPDIR)/modules/sound.o - + ln -fs ../drivers/sound/sound.o $(TOPDIR)/modules/sound.o lowlevel/lowlevel.o: dummy - cd lowlevel;make + cd lowlevel; make ifdef USE_DEPEND # diff -u --recursive --new-file v2.0.35/linux/drivers/sound/Readme linux/drivers/sound/Readme --- v2.0.35/linux/drivers/sound/Readme Sun Aug 31 08:36:50 1997 +++ linux/drivers/sound/Readme Sun Nov 15 10:33:11 1998 @@ -157,7 +157,7 @@ with impossible parameters. Check that the application is for sound driver version 2.X or later. -In general the printout of of /dev/sndstat should tell what is the problem. +In general the printout of /dev/sndstat should tell what is the problem. It's possible that there are bugs in the sound driver but 99% of the problems reported to me are caused by somehow incorrect setup during "make config". diff -u --recursive --new-file v2.0.35/linux/drivers/sound/ad1848_mixer.h linux/drivers/sound/ad1848_mixer.h --- v2.0.35/linux/drivers/sound/ad1848_mixer.h Sun Jun 30 01:43:36 1996 +++ linux/drivers/sound/ad1848_mixer.h Sun Nov 15 10:33:11 1998 @@ -17,7 +17,7 @@ * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. * Soundcard manufacturers have connected actual inputs (CD, synth, line, * etc) to these inputs in different order. Therefore it's difficult - * to assign mixer channels to to these inputs correctly. The following + * to assign mixer channels to these inputs correctly. The following * contains two alternative mappings. The first one is for GUS MAX and * the second is just a generic one (line1, line2 and line3). * (Actually this is not a mapping but rather some kind of interleaving diff -u --recursive --new-file v2.0.35/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c --- v2.0.35/linux/drivers/sound/sb_common.c Mon Jul 13 13:46:37 1998 +++ linux/drivers/sound/sb_common.c Sun Nov 15 10:33:11 1998 @@ -1196,11 +1196,13 @@ case MDL_SB16: if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330) { - printk ("SB16: Invalid MIDI port %x\n", hw_config->irq); + printk ("SB16: Invalid MIDI port %x\n", hw_config->io_base); return 0; } hw_config->name = "Sound Blaster 16"; hw_config->irq = -devc->irq; + hw_config->dma = -1; + hw_config->dma2 = -1; sb16_set_mpu_port(devc, hw_config); break; diff -u --recursive --new-file v2.0.35/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c --- v2.0.35/linux/drivers/sound/sequencer.c Mon Aug 25 13:25:04 1997 +++ linux/drivers/sound/sequencer.c Sun Nov 15 10:33:11 1998 @@ -1149,7 +1149,10 @@ } if (!max_synthdev && !max_mididev) - return -(ENXIO); + { + sequencer_busy = 0; + return -(ENXIO); + } synth_open_mask = 0; diff -u --recursive --new-file v2.0.35/linux/fs/block_dev.c linux/fs/block_dev.c --- v2.0.35/linux/fs/block_dev.c Tue Mar 11 14:35:16 1997 +++ linux/fs/block_dev.c Sun Nov 15 10:33:11 1998 @@ -203,8 +203,11 @@ blocks = rblocks; } - if (block + blocks > size) + if (block + blocks > size) { blocks = size - block; + if (blocks == 0) + return 0; + } /* We do this in a two stage process. We first try to request as many blocks as we can, then we wait for the first one to diff -u --recursive --new-file v2.0.35/linux/fs/buffer.c linux/fs/buffer.c --- v2.0.35/linux/fs/buffer.c Sun Nov 15 10:49:47 1998 +++ linux/fs/buffer.c Sun Nov 15 10:33:11 1998 @@ -1005,11 +1005,22 @@ * This is no longer true, it is GFP_BUFFER again, the * swapping code now knows not to perform I/O when that * GFP level is specified... -DaveM + * + * Ouch, another bug! get_free_page() does not call + * try_to_free_page() if priority == GFP_BUFFER. This + * lets kswapd get into a lockup situation if there is + * no free space for buffer growth but we need more + * memory for a buffer_head for swapping. If memory is + * full of recyclable buffers, we deadlock because + * kswapd won't recycle them! Use GFP_IO instead: it + * still won't recurse (GFP_IO sets can_do_io to zero in + * try_to_free_page), but it lets us recover those + * buffer heads. --sct */ /* we now use kmalloc() here instead of gfp as we want to be able to easily release buffer heads - they took up quite a bit of memory (tridge) */ - bh = (struct buffer_head *) kmalloc(sizeof(*bh),GFP_BUFFER); + bh = (struct buffer_head *) kmalloc(sizeof(*bh),GFP_IO); if (bh) { put_unused_buffer_head(bh); nr_buffer_heads++; diff -u --recursive --new-file v2.0.35/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c --- v2.0.35/linux/fs/ext2/ialloc.c Sun Nov 15 10:49:47 1998 +++ linux/fs/ext2/ialloc.c Sun Nov 15 10:33:12 1998 @@ -205,14 +205,30 @@ inode->i_nlink); return; } - if (!inode->i_sb) { + sb = inode->i_sb; + if (!sb) { printk("ext2_free_inode: inode on nonexistent device\n"); return; } ext2_debug ("freeing inode %lu\n", inode->i_ino); - sb = inode->i_sb; + /* We need to kill quota references now, before grabbing the + * superblock lock because writing the quota out to disk + * may need to lock the superblock as well. + * + * It is safe to do this early instead of the original + * places because we cannot be here in ext2_free_inode + * if any other references to this inode exist at all. + * + * Based upon a 2.1.x fix by Bill Hawes. --DaveM + */ + if (sb->dq_op) { + sb->dq_op->free_inode (inode, 1); + if (IS_WRITABLE (inode)) + sb->dq_op->drop(inode); + } + lock_super (sb); if (inode->i_ino < EXT2_FIRST_INO(sb) || inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) { @@ -249,8 +265,6 @@ ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - if (sb->dq_op) - sb->dq_op->free_inode (inode, 1); sb->s_dirt = 1; clear_inode (inode); unlock_super (sb); diff -u --recursive --new-file v2.0.35/linux/fs/fat/fatfs_syms.c linux/fs/fat/fatfs_syms.c --- v2.0.35/linux/fs/fat/fatfs_syms.c Mon Jul 13 13:46:37 1998 +++ linux/fs/fat/fatfs_syms.c Sun Nov 15 10:33:12 1998 @@ -6,7 +6,6 @@ */ #define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) #include -#include #include #include diff -u --recursive --new-file v2.0.35/linux/fs/fat/file.c linux/fs/fat/file.c --- v2.0.35/linux/fs/fat/file.c Mon Jul 13 13:46:37 1998 +++ linux/fs/fat/file.c Sun Nov 15 10:33:13 1998 @@ -324,6 +324,12 @@ #else if (count <= 0) return 0; #endif + if (filp->f_pos + count > 0x7FFFFFFFL) { + count = 0x7FFFFFFFL - filp->f_pos; + if (!count) + return -EFBIG; + } + error = carry = 0; for (start = buf; count || carry; count -= size) { while (!(sector = fat_smap(inode,filp->f_pos >> SECTOR_BITS))) diff -u --recursive --new-file v2.0.35/linux/fs/fat/misc.c linux/fs/fat/misc.c --- v2.0.35/linux/fs/fat/misc.c Mon Jul 13 13:46:37 1998 +++ linux/fs/fat/misc.c Sun Nov 15 10:33:13 1998 @@ -515,7 +515,7 @@ /* * raw_scan performs raw_scan_sector on any sector. * - * NOTE: raw_scan must not be used on a directory that is is the process of + * NOTE: raw_scan must not be used on a directory that is the process of * being created. */ diff -u --recursive --new-file v2.0.35/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- v2.0.35/linux/fs/isofs/inode.c Mon Jul 13 13:46:37 1998 +++ linux/fs/isofs/inode.c Sun Nov 15 10:33:13 1998 @@ -587,7 +587,7 @@ int i; if (block<0) { - printk("_isofs_bmap: block<0"); + printk("_isofs_bmap: block<0\n"); return 0; } @@ -597,7 +597,8 @@ * If we are beyond the end of this file, don't give out any * blocks. */ - if( b_off > inode->i_size ) + if( (b_off > inode->i_size) || + ((b_off == inode->i_size) && (b_off & (PAGE_SIZE - 1))) ) { off_t max_legal_read_offset; @@ -614,7 +615,7 @@ if( b_off >= max_legal_read_offset ) { - printk("_isofs_bmap: block>= EOF(%d, %ld)", block, + printk("_isofs_bmap: block>= EOF(%d, %ld)\n", block, inode->i_size); } return 0; diff -u --recursive --new-file v2.0.35/linux/fs/isofs/joliet.c linux/fs/isofs/joliet.c --- v2.0.35/linux/fs/isofs/joliet.c Mon Jul 13 13:46:37 1998 +++ linux/fs/isofs/joliet.c Sun Nov 15 10:33:13 1998 @@ -7,7 +7,6 @@ */ #include -#include #include #include #include @@ -95,6 +94,13 @@ } if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) { len -= 2; + } + + /* + * Windows doesn't like periods at the end of a name + */ + while (len >= 2 && (outname[len-1] == '.')) { + len--; } if (inode->i_sb->u.isofs_sb.s_name_check == 'r') { diff -u --recursive --new-file v2.0.35/linux/fs/locks.c linux/fs/locks.c --- v2.0.35/linux/fs/locks.c Mon Jul 13 13:46:37 1998 +++ linux/fs/locks.c Sun Nov 15 10:33:13 1998 @@ -177,7 +177,7 @@ /* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in * the order they blocked. The documentation doesn't require this but - * it seems seems like the reasonable thing to do. + * it seems like the reasonable thing to do. */ static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter) diff -u --recursive --new-file v2.0.35/linux/fs/msdos/namei.c linux/fs/msdos/namei.c --- v2.0.35/linux/fs/msdos/namei.c Mon Jul 13 13:46:38 1998 +++ linux/fs/msdos/namei.c Sun Nov 15 10:33:13 1998 @@ -369,7 +369,7 @@ if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ pos = 0; bh = NULL; - while (fat_get_entry(dir,&pos,&bh,&de) > -1) + while (fat_get_entry(dir,&pos,&bh,&de) > -1) { /* Ignore vfat longname entries */ if (de->attr == ATTR_EXT) continue; @@ -379,6 +379,7 @@ fat_brelse(sb, bh); return -ENOTEMPTY; } + } if (bh) fat_brelse(sb, bh); } diff -u --recursive --new-file v2.0.35/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.0.35/linux/fs/nfs/dir.c Sun Nov 15 10:49:49 1998 +++ linux/fs/nfs/dir.c Sun Nov 15 10:33:14 1998 @@ -8,6 +8,11 @@ * 10 Apr 1996 Added silly rename for unlink --okir */ +/* + * Fixes: + * Ion Badulescu : FIFO's need special handling in NFSv2 + */ + #include #include #include @@ -443,7 +448,10 @@ iput(dir); return -ENAMETOOLONG; } - sattr.mode = mode; + if (S_ISFIFO(mode)) + sattr.mode = (mode & ~S_IFMT) | S_IFCHR; + else + sattr.mode = mode; sattr.uid = sattr.gid = (unsigned) -1; if (S_ISCHR(mode) || S_ISBLK(mode)) sattr.size = rdev; /* get out your barf bag */ @@ -452,6 +460,11 @@ sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), name, &sattr, &fhandle, &fattr); + if (error == -EINVAL && (S_ISFIFO(mode))) { + sattr.mode = mode; + error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), + name, &sattr, &fhandle, &fattr); + } if (!error) { nfs_lookup_cache_add(dir, name, &fhandle, &fattr); diff -u --recursive --new-file v2.0.35/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.0.35/linux/fs/nfs/inode.c Sat Nov 30 02:21:21 1996 +++ linux/fs/nfs/inode.c Sun Nov 15 10:33:14 1998 @@ -344,6 +344,9 @@ current->session = 1; current->pgrp = 1; sprintf(current->comm, "nfsiod"); +#ifndef MODULE + current->blocked = ~0UL; +#endif ret = nfsiod(); MOD_DEC_USE_COUNT; return ret; diff -u --recursive --new-file v2.0.35/linux/fs/nfs/nfsiod.c linux/fs/nfs/nfsiod.c --- v2.0.35/linux/fs/nfs/nfsiod.c Sat Jun 29 02:00:46 1996 +++ linux/fs/nfs/nfsiod.c Sun Nov 15 10:33:14 1998 @@ -99,10 +99,14 @@ /* Wait until user enqueues request */ dprintk("BIO: before: now %d nfsiod's active\n", active); dprintk("BIO: nfsiod %d waiting\n", current->pid); +#ifndef MODULE + current->signal = 0; +#endif interruptible_sleep_on(&req->rq_wait); - +#ifdef MODULE if (current->signal & ~current->blocked) break; +#endif if (!req->rq_rpcreq.rq_slot) continue; dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n", diff -u --recursive --new-file v2.0.35/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c --- v2.0.35/linux/fs/nfs/nfsroot.c Sun Nov 15 10:49:49 1998 +++ linux/fs/nfs/nfsroot.c Sun Nov 15 10:33:14 1998 @@ -16,6 +16,7 @@ * * Changes: * + * R. Drahtmueller : Set IFF_MULTICAST in dev->flags if applicable. * Alan Cox : Removed get_address name clash with FPU. * Alan Cox : Reformatted a bit. * Gero Kuhlmann : Code cleanup @@ -179,7 +180,11 @@ (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) { /* First up the interface */ old_flags = dev->flags; +#ifdef CONFIG_IP_MULTICAST + dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST; +#else dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; +#endif if (!(old_flags & IFF_UP) && dev_open(dev)) { dev->flags = old_flags; continue; diff -u --recursive --new-file v2.0.35/linux/fs/nfs/proc.c linux/fs/nfs/proc.c --- v2.0.35/linux/fs/nfs/proc.c Mon Jul 13 13:46:38 1998 +++ linux/fs/nfs/proc.c Sun Nov 15 10:33:14 1998 @@ -24,6 +24,11 @@ */ /* + * Fixes: + * Ion Badulescu : FIFO's need special handling in NFSv2 + */ + +/* * Defining NFS_PROC_DEBUG causes a lookup of a file named * "xyzzy" to toggle debugging. Just cd to an NFS-mounted * filesystem and type 'ls xyzzy' to turn on debugging. @@ -180,6 +185,11 @@ fattr->mtime.useconds = ntohl(*p++); fattr->ctime.seconds = ntohl(*p++); fattr->ctime.useconds = ntohl(*p++); + if (fattr->type == NFCHR && fattr->rdev == NFS_FIFO_DEV) { + fattr->type = NFFIFO; + fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; + fattr->rdev = 0; + } return p; } diff -u --recursive --new-file v2.0.35/linux/fs/pipe.c linux/fs/pipe.c --- v2.0.35/linux/fs/pipe.c Wed Aug 6 17:52:01 1997 +++ linux/fs/pipe.c Sun Nov 15 10:33:14 1998 @@ -413,7 +413,7 @@ int error; int i,j; - error = ENFILE; + error = -ENFILE; f1 = get_empty_filp(); if (!f1) goto no_files; diff -u --recursive --new-file v2.0.35/linux/fs/proc/array.c linux/fs/proc/array.c --- v2.0.35/linux/fs/proc/array.c Sun Nov 15 10:49:49 1998 +++ linux/fs/proc/array.c Sun Nov 15 10:33:14 1998 @@ -29,7 +29,7 @@ * Yves Arrouye : remove removal of trailing spaces in get_array. * * - * Alan Cox : security fixes. a + * Alan Cox : security fixes. */ #include @@ -1074,7 +1074,7 @@ case PROC_PID_CMDLINE: return 0; } - if(current->fsuid == (*p)->euid) + if(suser() || current->fsuid == (*p)->euid) return 0; return 1; } diff -u --recursive --new-file v2.0.35/linux/fs/proc/mem.c linux/fs/proc/mem.c --- v2.0.35/linux/fs/proc/mem.c Wed Sep 11 07:57:16 1996 +++ linux/fs/proc/mem.c Sun Nov 15 10:33:14 1998 @@ -219,7 +219,7 @@ pgd_t *src_dir, *dest_dir; pmd_t *src_middle, *dest_middle; pte_t *src_table, *dest_table; - unsigned long stmp, dtmp; + unsigned long stmp, dtmp, mapnr; struct vm_area_struct *src_vma = NULL; /* Get the source's task information */ @@ -299,7 +299,9 @@ set_pte(src_table, pte_mkdirty(*src_table)); set_pte(dest_table, *src_table); - mem_map[MAP_NR(pte_page(*src_table))].count++; + mapnr = MAP_NR(pte_page(*src_table)); + if (mapnr < MAP_NR(high_memory)) + mem_map[mapnr].count++; stmp += PAGE_SIZE; dtmp += PAGE_SIZE; diff -u --recursive --new-file v2.0.35/linux/fs/read_write.c linux/fs/read_write.c --- v2.0.35/linux/fs/read_write.c Tue Aug 20 23:18:09 1996 +++ linux/fs/read_write.c Sun Nov 15 10:33:14 1998 @@ -155,7 +155,12 @@ if (!file->f_op || !file->f_op->write) goto out; error = 0; - if (!count) + /* + * If this was a development kernel we'd just drop the test + * its not so we do this for stricter compatibility both to + * applications and drivers. + */ + if (!count && !IS_ZERO_WR(inode)) goto out; error = locks_verify_area(FLOCK_VERIFY_WRITE,inode,file,file->f_pos,count); if (error) @@ -268,6 +273,10 @@ fn = file->f_op->read; if (type == VERIFY_READ) fn = (IO_fn_t) file->f_op->write; + + if(fn==NULL) + return -EOPNOTSUPP; + vector = iov; while (count > 0) { void * base; diff -u --recursive --new-file v2.0.35/linux/fs/select.c linux/fs/select.c --- v2.0.35/linux/fs/select.c Mon Jul 13 13:46:38 1998 +++ linux/fs/select.c Sun Nov 15 10:33:14 1998 @@ -46,7 +46,7 @@ * Linus noticed. -- jrs */ -static void free_wait(select_table * p) +void select_free_wait(select_table * p) { struct select_table_entry * entry = p->entry + p->nr; @@ -96,7 +96,7 @@ * and we aren't going to sleep on the select_table. -- jrs */ -static int check(int flag, select_table * wait, struct file * file) +int select_check(int flag, select_table * wait, struct file * file) { struct inode * inode; struct file_operations *fops; @@ -180,17 +180,17 @@ struct file * file = current->files->fd[i]; if (!file) continue; - if (FD_ISSET(i,in) && check(SEL_IN,wait,file)) { + if (FD_ISSET(i,in) && select_check(SEL_IN,wait,file)) { FD_SET(i, res_in); count++; wait = NULL; } - if (FD_ISSET(i,out) && check(SEL_OUT,wait,file)) { + if (FD_ISSET(i,out) && select_check(SEL_OUT,wait,file)) { FD_SET(i, res_out); count++; wait = NULL; } - if (FD_ISSET(i,ex) && check(SEL_EX,wait,file)) { + if (FD_ISSET(i,ex) && select_check(SEL_EX,wait,file)) { FD_SET(i, res_ex); count++; wait = NULL; @@ -201,7 +201,7 @@ schedule(); goto repeat; } - free_wait(&wait_table); + select_free_wait(&wait_table); free_page((unsigned long) entry); current->state = TASK_RUNNING; bale: diff -u --recursive --new-file v2.0.35/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c --- v2.0.35/linux/fs/smbfs/proc.c Mon Jul 13 13:46:38 1998 +++ linux/fs/smbfs/proc.c Sun Nov 15 10:33:14 1998 @@ -205,13 +205,13 @@ static int utc2local(int time) { - return time - sys_tz.tz_minuteswest * 60; + return time - sys_tz.tz_minuteswest * 60 + sys_tz.tz_dsttime * 3600; } static int local2utc(int time) { - return time + sys_tz.tz_minuteswest * 60; + return time + sys_tz.tz_minuteswest * 60 - sys_tz.tz_dsttime * 3600; } /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ @@ -1221,15 +1221,15 @@ WSET(param, 10, 8 + 4 + 2); /* resume required + close on end + continue */ + } #ifdef CONFIG_SMB_WIN95 - /* Windows 95 is not able to deliver answers - to FIND_NEXT fast enough, so sleep 0.2 seconds */ - current->timeout = jiffies + HZ / 5; - current->state = TASK_INTERRUPTIBLE; - schedule(); - current->timeout = 0; + /* Windows 95 is not able to deliver answers + to FIND_NEXT fast enough, so sleep 0.2 seconds */ + current->timeout = jiffies + HZ / 5; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; #endif - } result = smb_trans2_request(server, command, 0, NULL, 12 + mask_len + 2, param, diff -u --recursive --new-file v2.0.35/linux/fs/super.c linux/fs/super.c --- v2.0.35/linux/fs/super.c Sun Nov 15 10:49:49 1998 +++ linux/fs/super.c Sun Nov 15 10:33:15 1998 @@ -973,7 +973,9 @@ #ifdef CONFIG_BLK_DEV_FD if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { +#ifdef CONFIG_BLK_DEV_INITRD extern int rd_doload; +#endif floppy_eject(); #ifndef CONFIG_BLK_DEV_RAM printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)\n"); diff -u --recursive --new-file v2.0.35/linux/fs/vfat/namei.c linux/fs/vfat/namei.c --- v2.0.35/linux/fs/vfat/namei.c Mon Jul 13 13:46:39 1998 +++ linux/fs/vfat/namei.c Sun Nov 15 10:33:15 1998 @@ -10,7 +10,6 @@ * the problem, send a script that demonstrates it. */ -#include #define __NO_VERSION__ #include @@ -832,6 +831,9 @@ if ((vf->len != name_len + 1) || (vf->name[name_len] != '.')) { return 0; } + if (name_len == 2 && name[0] == '.' && name[1] == '.') { + return 0; + } } s1 = name; s2 = vf->name; @@ -967,7 +969,6 @@ sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot); sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots; res = 0; - return 0; } else { res = -ENOENT; } diff -u --recursive --new-file v2.0.35/linux/include/asm-alpha/lca.h linux/include/asm-alpha/lca.h --- v2.0.35/linux/include/asm-alpha/lca.h Mon Jul 13 13:46:40 1998 +++ linux/include/asm-alpha/lca.h Sun Nov 15 10:33:15 1998 @@ -1,3 +1,4 @@ +#include /* CONFIG_ALPHA_SRM_SETUP */ #ifndef __ALPHA_LCA__H__ #define __ALPHA_LCA__H__ diff -u --recursive --new-file v2.0.35/linux/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h --- v2.0.35/linux/include/asm-alpha/unistd.h Thu Apr 3 20:00:00 1997 +++ linux/include/asm-alpha/unistd.h Sun Nov 15 10:33:15 1998 @@ -274,8 +274,8 @@ return sys_write(fd, buf, nr); } -extern int sys_read(int, char *, int); -static inline int read(int fd, char * buf, int nr) +extern int sys_read(unsigned int, char *, int); +static inline int read(unsigned int fd, char * buf, int nr) { return sys_read(fd, buf, nr); } diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h --- v2.0.35/linux/include/asm-i386/bugs.h Tue Dec 2 13:52:32 1997 +++ linux/include/asm-i386/bugs.h Sun Nov 15 10:33:15 1998 @@ -145,9 +145,192 @@ } } +/* + * B step AMD K6 before B 9730xxxx have hardware bugs that can cause + * misexecution of code under Linux. Owners of such processors should + * contact AMD for precise details and a (free) CPU exchange. + * + * See http://www.chorus.com/~poulot/k6bug.html + * http://www.amd.com/K6/k6docs/revgd.html + * + * The following test is erm... interesting. AMD neglected to up + * the chip stepping when fixing the bug but they also tweaked some + * performance at the same time... + */ + +extern void vide(void); +__asm__(".align 4\nvide: ret"); + +static void check_k6_bug(void) +{ + + if ((strcmp(x86_vendor_id, "AuthenticAMD") == 0) && + (x86_model == 6) && (x86_mask == 1)) + { + int n; + void (*f_vide)(void); + unsigned long d, d2; + + printk(KERN_INFO "AMD K6 stepping B detected - "); + +#define K6_BUG_LOOP 1000000 + + /* + * It looks like AMD fixed the 2.6.2 bug and improved indirect + * calls at the same time. + */ + + n = K6_BUG_LOOP; + f_vide = vide; + __asm__ ("rdtsc" : "=a" (d)); + while (n--) + f_vide(); + __asm__ ("rdtsc" : "=a" (d2)); + d = d2-d; + + if (d > 20*K6_BUG_LOOP) { + printk("system stability may be impaired when more than 32 MB are used.\n"); + } + else + printk("probably OK (after B9730xxxx).\n"); + } +} + +/* Cyrix stuff from this point on */ + +/* Cyrix 5/2 test (return 0x200 if it's a Cyrix) */ +static inline int test_cyrix_52div(void) +{ + int test; + + __asm__ __volatile__("xor %%eax,%%eax\n\t" + "sahf\n\t" + "movb $5,%%al\n\t" + "movb $2,%%bl\n\t" + "div %%bl\n\t" + "lahf\n\t" + "andl $0xff00,%%eax": "=eax" (test) : : "bx"); + + return test; +} + +/* test for CCR3 bit 7 r/w */ +static char test_cyrix_cr3rw(void) +{ + char temp, test; + + temp = getCx86(CX86_CCR3); /* get current CCR3 value */ + setCx86(CX86_CCR3, temp ^ 0x80); /* toggle test bit and write */ + getCx86(0xc0); /* dummy to change bus */ + test = temp - getCx86(CX86_CCR3); /* != 0 if ccr3 r/w */ + setCx86(CX86_CCR3, temp); /* return CCR3 to original value */ + + return test; +} + +/* redo the cpuid test in head.S, so that those 6x86(L) now get + detected properly (0 == no cpuid) */ +static inline int test_cpuid(void) +{ + int test; + + __asm__("pushfl\n\t" + "popl %%eax\n\t" + "movl %%eax,%%ecx\n\t" + "xorl $0x200000,%%eax\n\t" + "pushl %%eax\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %%eax\n\t" + "xorl %%ecx,%%eax\n\t" + "pushl %%ecx\n\t" + "popfl" : "=eax" (test) : : "cx"); + + return test; +} + +/* All Cyrix 6x86 and 6x86L need the SLOP bit reset so that the udelay loop + * calibration works well. + * This routine must be called with MAPEN enabled, otherwise we don't + * have access to CCR5. + */ + +static void check_6x86_slop(void) +{ + if (x86_model == 2) /* if 6x86 or 6x86L */ + setCx86(CX86_CCR5, getCx86(CX86_CCR5) & 0xfd); /* reset SLOP */ +} + +/* Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected + * by the fact that they preserve the flags across the division of 5/2. + * PII and PPro exhibit this behavior too, but they have cpuid available. + */ + +static void check_cyrix_various(void) +{ + if ((x86 == 4) && (test_cyrix_52div()==0x200)) + { + /* if it's a Cyrix */ + + unsigned long flags; + + /* default to an "old" Cx486 */ + strcpy(x86_vendor_id, "CyrixInstead"); + x86_model = -1; + x86_mask = 0; + + /* Disable interrupts */ + save_flags(flags); + cli(); + + /* First check for very old CX486 models */ + /* that did not have DIR0/DIR1. */ + if (test_cyrix_cr3rw()) + { /* if has DIR0/DIR1 */ + + char ccr3; + char dir0; + x86_model = 0; + + /* Enable MAPEN */ + ccr3 = getCx86(CX86_CCR3); + setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); + + dir0 = getCx86(CX86_DIR0); + if ((dir0 & 0xf0) == 0x30) /* Use DIR0 to determine if this is a 6x86 class processor */ + { + /* try enabling cpuid */ + setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80); + } + + if (test_cpuid()) + { + int eax, dummy; + + /* get processor info */ + + cpuid(1, &eax, &dummy, &dummy, + &x86_capability); + + have_cpuid = 1; + x86_model = (eax >> 4) & 0xf; + x86 = (eax >> 8) & 0xf; + check_6x86_slop(); + } + /* disable MAPEN */ + setCx86(CX86_CCR3, ccr3); + } /* endif has DIR0/DIR1 */ + sti(); + restore_flags(flags); /* restore interrupt state */ + } /* endif it's a Cyrix */ +} + +/* Check various processor bugs */ static void check_bugs(void) { + check_cyrix_various(); + check_k6_bug(); check_tlb(); check_fpu(); check_hlt(); diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h --- v2.0.35/linux/include/asm-i386/irq.h Mon Jul 13 13:46:41 1998 +++ linux/include/asm-i386/irq.h Sun Nov 15 10:33:15 1998 @@ -108,6 +108,17 @@ "1:\tjmp 1f\n" \ "1:\toutb %al,$0x20\n\t" +/* do not modify the ISR nor the cache_A1 variable */ +#define MSGACK_SECOND(mask,nr) \ + "inb $0xA1,%al\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\tmovb $0x20,%al\n\t" \ + "outb %al,$0xA0\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\toutb %al,$0x20\n\t" + #define UNBLK_FIRST(mask) \ "inb $0x21,%al\n\t" \ "jmp 1f\n" \ @@ -162,6 +173,8 @@ "jnc 3f\n\t" \ "cmpb "SYMBOL_NAME_STR(active_kernel_processor)", %al\n\t" \ "je 4f\n\t" \ + "cmpb "SYMBOL_NAME_STR(boot_cpu_id)", %al\n\t" \ + "jne 2f\n\t" \ "movb $1, "SYMBOL_NAME_STR(smp_blocked_interrupt_pending)"\n\t" \ "2: " \ SMP_PROF_INT_SPINS \ @@ -190,7 +203,10 @@ "movb %al, "SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \ "4: " \ "incl "SYMBOL_NAME_STR(kernel_counter)"\n\t" \ + "cmpb "SYMBOL_NAME_STR(boot_cpu_id)", %al\n\t" \ + "jne 7f\n\t" \ "movb $0, "SYMBOL_NAME_STR(smp_blocked_interrupt_pending)"\n\t" \ + "7: " \ "popfl\n\t" \ "popl %edx\n\t" \ "popl %ecx\n\t" \ @@ -302,34 +318,14 @@ __asm__( \ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \ - "pushl $-"#nr"-2\n\t" \ - SAVE_ALL \ - ENTER_KERNEL \ - ACK_##chip(mask,(nr&7)) \ - "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\ - "sti\n\t" \ - "movl %esp,%ebx\n\t" \ - "pushl %ebx\n\t" \ - "pushl $" #nr "\n\t" \ - "call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \ - "addl $8,%esp\n\t" \ - "cli\n\t" \ - UNBLK_##chip(mask) \ - GET_PROCESSOR_ID \ - "btrl $" STR(SMP_FROM_INT) ","SYMBOL_NAME_STR(smp_proc_in_lock)"(,%eax,4)\n\t" \ - "decl "SYMBOL_NAME_STR(intr_count)"\n\t" \ - "incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \ - "jmp ret_from_sys_call\n" \ -"\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \ SAVE_MOST \ - ACK_##chip(mask,(nr&7)) \ + MSGACK_##chip(mask,(nr&7)) \ SMP_PROF_IPI_CNT \ "pushl $" #nr "\n\t" \ "call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \ "addl $4,%esp\n\t" \ "cli\n\t" \ - UNBLK_##chip(mask) \ RESTORE_MOST \ "\n"__ALIGN_STR"\n" \ SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \ diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/mtrr.h linux/include/asm-i386/mtrr.h --- v2.0.35/linux/include/asm-i386/mtrr.h Wed Dec 31 16:00:00 1969 +++ linux/include/asm-i386/mtrr.h Sun Nov 15 10:33:15 1998 @@ -0,0 +1,65 @@ +/* Generic MTRR (Memory Type Range Register) ioctls. + + Copyright (C) 1997 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + modified by Mathias Fr"ohlich, Jan, 1998 + +*/ +#ifndef _LINUX_MTRR_H +#define _LINUX_MTRR_H + +/* These are the region types */ +#define MTRR_TYPE_UNCACHABLE 0 +#define MTRR_TYPE_WRCOMB 1 +/*#define MTRR_TYPE_ 2*/ +/*#define MTRR_TYPE_ 3*/ +#define MTRR_TYPE_WRTHROUGH 4 +#define MTRR_TYPE_WRPROT 5 +#define MTRR_TYPE_WRBACK 6 +#define MTRR_NUM_TYPES 7 + +static char *attrib_to_str (int x) __attribute__ ((unused)); + +static char *attrib_to_str (int x) +{ + switch (x) { + case 0: return "uncachable"; + case 1: return "write-combining"; + case 4: return "write-through"; + case 5: return "write-protect"; + case 6: return "write-back"; + default: return "?"; + } +} /* End Function attrib_to_str */ + +#ifdef __KERNEL__ + +#ifdef CONFIG_MTRR + +extern void check_mtrr_config(void); +extern void init_mtrr_config(void); +/* extern void set_mtrr_config(void); */ + +#endif /* CONFIG_MTRR */ + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_MTRR_H */ diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h --- v2.0.35/linux/include/asm-i386/processor.h Mon Jul 13 13:46:41 1998 +++ linux/include/asm-i386/processor.h Sun Nov 15 10:33:15 1998 @@ -27,6 +27,53 @@ extern char hlt_works_ok; /* problems on some 486Dx4's and old 386's */ extern int have_cpuid; /* We have a CPUID */ +extern unsigned long cpu_hz; /* CPU clock frequency from time.c */ + +/* + * Detection of CPU model (CPUID). + */ +extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) +{ + __asm__("cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "a" (op) + : "cc"); +} + +/* + * Cyrix CPU register indexes (use special macros to access these) + */ +#define CX86_CCR2 0xc2 +#define CX86_CCR3 0xc3 +#define CX86_CCR4 0xe8 +#define CX86_CCR5 0xe9 +#define CX86_DIR0 0xfe +#define CX86_DIR1 0xff + +/* + * Cyrix CPU register access macros + */ + +extern inline unsigned char getCx86(unsigned char reg) +{ + unsigned char data; + + __asm__ __volatile__("movb %1,%%al\n\t" + "outb %%al,$0x22\n\t" + "inb $0x23,%%al" : "=a" (data) : "q" (reg)); + return data; +} + +extern inline void setCx86(unsigned char reg, unsigned char data) +{ + __asm__ __volatile__("outb %%al,$0x22\n\t" + "movb %1,%%al\n\t" + "outb %%al,$0x23" : : "a" (reg), "q" (data)); +} + /* * Bus types (default is ISA, but people can check others with these..) * MCA_bus hardcoded to 0 for now. diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/ptrace.h linux/include/asm-i386/ptrace.h --- v2.0.35/linux/include/asm-i386/ptrace.h Sun Nov 15 10:49:51 1998 +++ linux/include/asm-i386/ptrace.h Sun Nov 15 10:33:16 1998 @@ -53,6 +53,8 @@ #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs)) #define instruction_pointer(regs) ((regs)->eip) extern void show_regs(struct pt_regs *); +struct task_struct; +extern void get_pt_regs_for_task(struct pt_regs *, struct task_struct *p); #endif #endif diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h --- v2.0.35/linux/include/asm-i386/smp.h Fri Apr 26 00:37:21 1996 +++ linux/include/asm-i386/smp.h Sun Nov 15 10:33:16 1998 @@ -160,6 +160,7 @@ char x86_mask; char x86_vendor_id[16]; int x86_capability; + int x86_ext_capability; int fdiv_bug; int have_cpuid; char wp_works_ok; diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/system.h linux/include/asm-i386/system.h --- v2.0.35/linux/include/asm-i386/system.h Mon Mar 17 15:00:50 1997 +++ linux/include/asm-i386/system.h Sun Nov 15 10:33:16 1998 @@ -296,4 +296,39 @@ void disable_hlt(void); void enable_hlt(void); +static __inline__ unsigned long long rdmsr(unsigned int msr) +{ + unsigned long long ret; + __asm__ __volatile__("rdmsr" + : "=A" (ret) + : "c" (msr)); + return ret; +} + +static __inline__ void wrmsr(unsigned int msr,unsigned long long val) +{ + __asm__ __volatile__("wrmsr" + : /* no Outputs */ + : "c" (msr), "A" (val)); +} + + +static __inline__ unsigned long long rdtsc(void) +{ + unsigned long long ret; + __asm__ __volatile__("rdtsc" + : "=A" (ret) + : /* no inputs */); + return ret; +} + +static __inline__ unsigned long long rdpmc(unsigned int counter) +{ + unsigned long long ret; + __asm__ __volatile__("rdpmc" + : "=A" (ret) + : "c" (counter)); + return ret; +} + #endif diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h --- v2.0.35/linux/include/asm-i386/unistd.h Thu Mar 21 22:34:02 1996 +++ linux/include/asm-i386/unistd.h Sun Nov 15 10:33:16 1998 @@ -169,6 +169,9 @@ #define __NR_sched_rr_get_interval 161 #define __NR_nanosleep 162 #define __NR_mremap 163 +#define __NR_poll 168 +#define __NR_getpmsg 188 +#define __NR_putpmsg 189 /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ #define _syscall0(type,name) \ @@ -275,6 +278,8 @@ static inline _syscall0(int,setup) static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) +static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) +static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall1(int,dup,int,fd) static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) diff -u --recursive --new-file v2.0.35/linux/include/asm-mips/ioctl.h linux/include/asm-mips/ioctl.h --- v2.0.35/linux/include/asm-mips/ioctl.h Wed Dec 13 02:39:45 1995 +++ linux/include/asm-mips/ioctl.h Sun Nov 15 10:33:16 1998 @@ -11,7 +11,7 @@ * the process. I'd like to clean it up for the i386 as well, but * it's so painful recognizing both the new and the old numbers.. * - * The same applies for for the MIPS ABI; in fact even the macros + * The same applies for the MIPS ABI; in fact even the macros * from Linux/Alpha fit almost perfectly. */ diff -u --recursive --new-file v2.0.35/linux/include/asm-mips/mipsregs.h linux/include/asm-mips/mipsregs.h --- v2.0.35/linux/include/asm-mips/mipsregs.h Wed Dec 13 02:39:46 1995 +++ linux/include/asm-mips/mipsregs.h Sun Nov 15 10:33:16 1998 @@ -238,7 +238,7 @@ /* * Bitfields and bit numbers in the coprocessor 0 cause register. * - * Refer to to your MIPS R4xx0 manual, chapter 5 for explanation. + * Refer to your MIPS R4xx0 manual, chapter 5 for explanation. */ #define CAUSEB_EXCCODE 2 #define CAUSEF_EXCCODE (31 << 2) diff -u --recursive --new-file v2.0.35/linux/include/linux/apm_bios.h linux/include/linux/apm_bios.h --- v2.0.35/linux/include/linux/apm_bios.h Wed Oct 15 15:22:05 1997 +++ linux/include/linux/apm_bios.h Sun Nov 15 10:33:16 1998 @@ -83,7 +83,7 @@ extern int apm_register_callback(int (*callback)(apm_event_t)); extern void apm_unregister_callback(int (*callback)(apm_event_t)); -extern int apm_set_power_state(ushort state); +extern void apm_power_off(void); extern int apm_display_blank(void); extern int apm_display_unblank(void); diff -u --recursive --new-file v2.0.35/linux/include/linux/b1lli.h linux/include/linux/b1lli.h --- v2.0.35/linux/include/linux/b1lli.h Mon Aug 4 17:34:01 1997 +++ linux/include/linux/b1lli.h Sun Nov 15 10:33:17 1998 @@ -1,11 +1,44 @@ /* - * $Id: b1lli.h,v 1.1 1997/03/04 21:27:32 calle Exp $ + * $Id: b1lli.h,v 1.1.2.10 1998/10/25 14:37:35 fritz Exp $ * * ISDN lowlevel-module for AVM B1-card. * * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: b1lli.h,v $ + * Revision 1.1.2.10 1998/10/25 14:37:35 fritz + * Backported from MIPS (Cobalt). + * + * Revision 1.1.2.9 1998/03/20 14:30:02 calle + * added cardnr to detect if you try to add same T1 to different io address. + * change number of nccis depending on number of channels. + * + * Revision 1.1.2.8 1998/03/04 17:32:33 calle + * Changes for T1. + * + * Revision 1.1.2.7 1998/02/27 15:38:29 calle + * T1 running with slow link. + * + * Revision 1.1.2.6 1998/02/24 17:57:36 calle + * changes for T1. + * + * Revision 1.1.2.5 1998/01/27 16:11:50 calle + * support for PCMCIA B1/M1/M2 ready. + * + * Revision 1.1.2.4 1998/01/26 14:51:56 calle + * interface change for pcmcia cards. + * + * Revision 1.1.2.3 1998/01/23 16:46:45 calle + * new functions for pcmcia cards. + * + * Revision 1.1.2.2 1997/11/26 16:57:26 calle + * more changes for B1/M1/T1. + * + * Revision 1.1.2.1 1997/11/26 10:47:01 calle + * prepared for M1 (Mobile) and T1 (PMX) cards. + * prepared to set configuration after load to support other D-channel + * protocols, point-to-point and leased lines. + * * Revision 1.1 1997/03/04 21:27:32 calle * First version in isdn4linux * @@ -32,10 +65,22 @@ avmb1_t4file t4file; } avmb1_loaddef; +typedef struct avmb1_loadandconfigdef { + int contr; + avmb1_t4file t4file; + avmb1_t4file t4config; +} avmb1_loadandconfigdef; + typedef struct avmb1_resetdef { int contr; } avmb1_resetdef; +typedef struct avmb1_getdef { + int contr; + int cardtype; + int cardstate; +} avmb1_getdef; + /* * struct for adding new cards */ @@ -44,33 +89,47 @@ int irq; } avmb1_carddef; -#define AVMB1_LOAD 0 /* load image to card */ -#define AVMB1_ADDCARD 1 /* add a new card */ -#define AVMB1_RESETCARD 2 /* reset a card */ +#define AVM_CARDTYPE_B1 0 +#define AVM_CARDTYPE_T1 1 +#define AVM_CARDTYPE_M1 2 +#define AVM_CARDTYPE_M2 3 +typedef struct avmb1_extcarddef { + int port; + int irq; + int cardtype; + int cardnr; /* for HEMA/T1 */ +} avmb1_extcarddef; + +#define AVMB1_LOAD 0 /* load image to card */ +#define AVMB1_ADDCARD 1 /* add a new card */ +#define AVMB1_RESETCARD 2 /* reset a card */ +#define AVMB1_LOAD_AND_CONFIG 3 /* load image and config to card */ +#define AVMB1_ADDCARD_WITH_TYPE 4 /* add a new card, with cardtype */ +#define AVMB1_GET_CARDINFO 5 /* get cardtype */ +#define AVMB1_REMOVECARD 6 /* remove a card (usefull for T1) */ -#ifdef __KERNEL__ /* * card states for startup */ -#define CARD_NONE 0 +#define CARD_FREE 0 #define CARD_DETECTED 1 #define CARD_LOADING 2 #define CARD_INITSTATE 4 #define CARD_RUNNING 5 #define CARD_ACTIVE 6 -#define AVMB1_PORTLEN 0x1f +#ifdef __KERNEL__ + +#define AVMB1_PORTLEN 0x1f -#define AVM_MAXVERSION 8 -#define AVM_NBCHAN 2 +#define AVM_MAXVERSION 8 -#define AVM_NAPPS 30 -#define AVM_NPLCI 5 -#define AVM_NNCCI 6 +#define AVM_NAPPS 30 +#define AVM_NNCCI_PER_CHANNEL 4 /* * Main driver data @@ -79,8 +138,10 @@ typedef struct avmb1_card { struct avmb1_card *next; int cnr; - unsigned short port; + unsigned int port; unsigned irq; + int cardtype; + int cardnr; /* for T1-HEMA */ volatile unsigned short cardstate; int interrupt; int blocked; @@ -108,22 +169,26 @@ /* b1lli.c */ -int B1_detect(unsigned short base); -void B1_reset(unsigned short base); -int B1_load_t4file(unsigned short base, avmb1_t4file * t4file); -int B1_loaded(unsigned short base); -unsigned char B1_assign_irq(unsigned short base, unsigned irq); -unsigned char B1_enable_irq(unsigned short base); -unsigned char B1_disable_irq(unsigned short base); -int B1_valid_irq(unsigned irq); +int B1_detect(unsigned int base, int cardtype); +int T1_detectandinit(unsigned int base, unsigned irq, int cardnr); +void B1_reset(unsigned int base); +void T1_reset(unsigned int base); +int B1_load_t4file(unsigned int base, avmb1_t4file * t4file); +int B1_load_config(unsigned int base, avmb1_t4file * config); +int B1_loaded(unsigned int base); +void B1_setinterrupt(unsigned int base, unsigned irq, int cardtype); +unsigned char B1_disable_irq(unsigned int base); +void T1_disable_irq(unsigned int base); +int B1_valid_irq(unsigned irq, int cardtype); +int B1_valid_port(unsigned port, int cardtype); void B1_handle_interrupt(avmb1_card * card); -void B1_send_init(unsigned short port, +void B1_send_init(unsigned int port, unsigned int napps, unsigned int nncci, unsigned int cardnr); -void B1_send_register(unsigned short port, +void B1_send_register(unsigned int port, __u16 appid, __u32 nmsg, __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize); -void B1_send_release(unsigned short port, __u16 appid); -void B1_send_message(unsigned short port, struct sk_buff *skb); +void B1_send_release(unsigned int port, __u16 appid); +void B1_send_message(unsigned int port, struct sk_buff *skb); /* b1capi.c */ void avmb1_handle_new_ncci(avmb1_card * card, @@ -133,8 +198,16 @@ void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb); void avmb1_card_ready(avmb1_card * card); -int avmb1_addcard(int port, int irq); -int avmb1_probecard(int port, int irq); +/* standard calls, with check and allocation of resources */ +int avmb1_addcard(int port, int irq, int cardtype); +int avmb1_probecard(int port, int irq, int cardtype); + +int avmb1_resetcard(int cardnr); + +/* calls for pcmcia driver */ +int avmb1_detectcard(int port, int irq, int cardtype); +int avmb1_registercard(int port, int irq, int cardtype, int allocio); +int avmb1_unregistercard(int cnr, int freeio); #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.0.35/linux/include/linux/cyclades.h linux/include/linux/cyclades.h --- v2.0.35/linux/include/linux/cyclades.h Sat Apr 20 11:09:56 1996 +++ linux/include/linux/cyclades.h Sun Nov 15 10:33:17 1998 @@ -1,14 +1,54 @@ -/* +/* $Revision: 2.6 $$Date: 1998/08/10 16:57:01 $ * linux/include/linux/cyclades.h * - * This file is maintained by Marcio Saito and + * This file is maintained by Ivan Passos , + * Marcio Saito and * Randolph Bentson . * * This file contains the general definitions for the cyclades.c driver *$Log: cyclades.h,v $ - * Revision 1.5 1995/11/13 21:13:31 bentson - * changes suggested by Michael Chastain - * to support use of this file in non-kernel applications + *Revision 2.5 1998/08/03 16:57:01 ivan + *added cyclades_idle_stats structure; + * + *Revision 2.4 1998/06/01 12:09:53 ivan + *removed closing_wait2 from cyclades_port structure; + * + *Revision 2.3 1998/03/16 18:01:12 ivan + *changes in the cyclades_port structure to get it closer to the + *standard serial port structure; + *added constants for new ioctls; + * + *Revision 2.2 1998/02/17 16:50:00 ivan + *changes in the cyclades_port structure (addition of shutdown_wait and + *chip_rev variables); + *added constants for new ioctls and for CD1400 rev. numbers. + * + *Revision 2.1 1997/10/24 16:03:00 ivan + *added rflow (which allows enabling the CD1400 special flow control + *feature) and rtsdtr_inv (which allows DTR/RTS pin inversion) to + *cyclades_port structure; + *added Alpha support + * + *Revision 2.0 1997/06/30 10:30:00 ivan + *added some new doorbell command constants related to IOCTLW and + *UART error signaling + * + *Revision 1.8 1997/06/03 15:30:00 ivan + *added constant ZFIRM_HLT + *added constant CyPCI_Ze_win ( = 2 * Cy_PCI_Zwin) + * + *Revision 1.7 1997/03/26 10:30:00 daniel + *new entries at the end of cyclades_port struct to reallocate + *variables illegally allocated within card memory. + * + *Revision 1.6 1996/09/09 18:35:30 bentson + *fold in changes for Cyclom-Z -- including structures for + *communicating with board as well modest changes to original + *structures to support new features. + * + *Revision 1.5 1995/11/13 21:13:31 bentson + *changes suggested by Michael Chastain + *to support use of this file in non-kernel applications * * */ @@ -23,6 +63,22 @@ unsigned long char_last; }; +/* + * These stats all reflect activity since the device was last initialized. + * (i.e., since the port was opened with no other processes already having it + * open) + */ +struct cyclades_idle_stats { + time_t in_use; /* Time device has been in use (secs) */ + time_t recv_idle; /* Time since last char received (secs) */ + time_t xmit_idle; /* Time since last char transmitted (secs) */ + unsigned long recv_bytes; /* Bytes received */ + unsigned long xmit_bytes; /* Bytes transmitted */ + unsigned long overruns; /* Input overruns */ + unsigned long frame_errs; /* Input framing errors */ + unsigned long parity_errs; /* Input parity errors */ +}; + #define CYCLADES_MAGIC 0x4359 #define CYGETMON 0x435901 @@ -34,17 +90,412 @@ #define CYSETTIMEOUT 0x435907 #define CYGETDEFTIMEOUT 0x435908 #define CYSETDEFTIMEOUT 0x435909 +#define CYSETRFLOW 0x43590a +#define CYGETRFLOW 0x43590b +#define CYSETRTSDTR_INV 0x43590c +#define CYGETRTSDTR_INV 0x43590d +#define CYZSETPOLLCYCLE 0x43590e +#define CYZGETPOLLCYCLE 0x43590f +#define CYGETCD1400VER 0x435910 +#define CYGETCARDINFO 0x435911 +#define CYSETWAIT 0x435912 +#define CYGETWAIT 0x435913 + +/*************** CYCLOM-Z ADDITIONS ***************/ + +#define CZIOC ('M' << 8) +#define CZ_NBOARDS (CZIOC|0xfa) +#define CZ_BOOT_START (CZIOC|0xfb) +#define CZ_BOOT_DATA (CZIOC|0xfc) +#define CZ_BOOT_END (CZIOC|0xfd) +#define CZ_TEST (CZIOC|0xfe) + +#define CZ_DEF_POLL (HZ/25) + +#define MAX_BOARD 4 /* Max number of boards */ +#define MAX_DEV 256 /* Max number of ports total */ +#define CYZ_MAX_SPEED 921600 + +#define CYZ_FIFO_SIZE 16 + +#define CYZ_BOOT_NWORDS 0x100 +struct CYZ_BOOT_CTRL { + unsigned short nboard; + int status[MAX_BOARD]; + int nchannel[MAX_BOARD]; + int fw_rev[MAX_BOARD]; + unsigned long offset; + unsigned long data[CYZ_BOOT_NWORDS]; +}; + + +#ifndef DP_WINDOW_SIZE +/* #include "cyclomz.h" */ +/****************** ****************** *******************/ +/* + * The data types defined below are used in all ZFIRM interface + * data structures. They accomodate differences between HW + * architectures and compilers. + */ + +#if defined(__alpha__) +typedef unsigned long ucdouble; /* 64 bits, unsigned */ +typedef unsigned int uclong; /* 32 bits, unsigned */ +#else +typedef unsigned long uclong; /* 32 bits, unsigned */ +#endif +typedef unsigned short ucshort; /* 16 bits, unsigned */ +typedef unsigned char ucchar; /* 8 bits, unsigned */ + +/* + * Memory Window Sizes + */ + +#define DP_WINDOW_SIZE (0x00080000) /* window size 512 Kb */ +#define ZE_DP_WINDOW_SIZE (0x00100000) /* window size 1 Mb (Ze and + 8Zo V.2 */ +#define CTRL_WINDOW_SIZE (0x00000080) /* runtime regs 128 bytes */ + +/* + * CUSTOM_REG - Cyclom-Z/PCI Custom Registers Set. The driver + * normally will access only interested on the fpga_id, fpga_version, + * start_cpu and stop_cpu. + */ + +struct CUSTOM_REG { + uclong fpga_id; /* FPGA Identification Register */ + uclong fpga_version; /* FPGA Version Number Register */ + uclong cpu_start; /* CPU start Register (write) */ + uclong cpu_stop; /* CPU stop Register (write) */ + uclong misc_reg; /* Miscelaneous Register */ + uclong idt_mode; /* IDT mode Register */ + uclong uart_irq_status; /* UART IRQ status Register */ + uclong clear_timer0_irq; /* Clear timer interrupt Register */ + uclong clear_timer1_irq; /* Clear timer interrupt Register */ + uclong clear_timer2_irq; /* Clear timer interrupt Register */ + uclong test_register; /* Test Register */ + uclong test_count; /* Test Count Register */ + uclong timer_select; /* Timer select register */ + uclong pr_uart_irq_status; /* Prioritized UART IRQ stat Reg */ + uclong ram_wait_state; /* RAM wait-state Register */ + uclong uart_wait_state; /* UART wait-state Register */ + uclong timer_wait_state; /* timer wait-state Register */ + uclong ack_wait_state; /* ACK wait State Register */ +}; + +/* + * RUNTIME_9060 - PLX PCI9060ES local configuration and shared runtime + * registers. This structure can be used to access the 9060 registers + * (memory mapped). + */ + +struct RUNTIME_9060 { + uclong loc_addr_range; /* 00h - Local Address Range */ + uclong loc_addr_base; /* 04h - Local Address Base */ + uclong loc_arbitr; /* 08h - Local Arbitration */ + uclong endian_descr; /* 0Ch - Big/Little Endian Descriptor */ + uclong loc_rom_range; /* 10h - Local ROM Range */ + uclong loc_rom_base; /* 14h - Local ROM Base */ + uclong loc_bus_descr; /* 18h - Local Bus descriptor */ + uclong loc_range_mst; /* 1Ch - Local Range for Master to PCI */ + uclong loc_base_mst; /* 20h - Local Base for Master PCI */ + uclong loc_range_io; /* 24h - Local Range for Master IO */ + uclong pci_base_mst; /* 28h - PCI Base for Master PCI */ + uclong pci_conf_io; /* 2Ch - PCI configuration for Master IO */ + uclong filler1; /* 30h */ + uclong filler2; /* 34h */ + uclong filler3; /* 38h */ + uclong filler4; /* 3Ch */ + uclong mail_box_0; /* 40h - Mail Box 0 */ + uclong mail_box_1; /* 44h - Mail Box 1 */ + uclong mail_box_2; /* 48h - Mail Box 2 */ + uclong mail_box_3; /* 4Ch - Mail Box 3 */ + uclong filler5; /* 50h */ + uclong filler6; /* 54h */ + uclong filler7; /* 58h */ + uclong filler8; /* 5Ch */ + uclong pci_doorbell; /* 60h - PCI to Local Doorbell */ + uclong loc_doorbell; /* 64h - Local to PCI Doorbell */ + uclong intr_ctrl_stat; /* 68h - Interrupt Control/Status */ + uclong init_ctrl; /* 6Ch - EEPROM control, Init Control, etc */ +}; + +/* Values for the Local Base Address re-map register */ + +#define WIN_RAM 0x00000001L /* set the sliding window to RAM */ +#define WIN_CREG 0x14000001L /* set the window to custom Registers */ + +/* Values timer select registers */ + +#define TIMER_BY_1M 0x00 /* clock divided by 1M */ +#define TIMER_BY_256K 0x01 /* clock divided by 256k */ +#define TIMER_BY_128K 0x02 /* clock divided by 128k */ +#define TIMER_BY_32K 0x03 /* clock divided by 32k */ + +/****************** ****************** *******************/ +#endif + +#ifndef ZFIRM_ID +/* #include "zfwint.h" */ +/****************** ****************** *******************/ +/* + * This file contains the definitions for interfacing with the + * Cyclom-Z ZFIRM Firmware. + */ + +/* General Constant definitions */ + +#define MAX_CHAN 64 /* max number of channels per board */ + +/* firmware id structure (set after boot) */ + +#define ID_ADDRESS 0x00000180L /* signature/pointer address */ +#define ZFIRM_ID 0x5557465AL /* ZFIRM/U signature */ +#define ZFIRM_HLT 0x59505B5CL /* ZFIRM needs external power supply */ +#define ZFIRM_RST 0x56040674L /* RST signal (due to FW reset) */ + +#define ZF_TINACT_DEF 1000 /* default inactivity timeout + (1000 ms) */ +#define ZF_TINACT ZF_TINACT_DEF + +struct FIRM_ID { + uclong signature; /* ZFIRM/U signature */ + uclong zfwctrl_addr; /* pointer to ZFW_CTRL structure */ +}; + +/* Op. System id */ + +#define C_OS_LINUX 0x00000030 /* generic Linux system */ + +/* channel op_mode */ + +#define C_CH_DISABLE 0x00000000 /* channel is disabled */ +#define C_CH_TXENABLE 0x00000001 /* channel Tx enabled */ +#define C_CH_RXENABLE 0x00000002 /* channel Rx enabled */ +#define C_CH_ENABLE 0x00000003 /* channel Tx/Rx enabled */ +#define C_CH_LOOPBACK 0x00000004 /* Loopback mode */ + +/* comm_parity - parity */ + +#define C_PR_NONE 0x00000000 /* None */ +#define C_PR_ODD 0x00000001 /* Odd */ +#define C_PR_EVEN 0x00000002 /* Even */ +#define C_PR_MARK 0x00000004 /* Mark */ +#define C_PR_SPACE 0x00000008 /* Space */ +#define C_PR_PARITY 0x000000ff + +#define C_PR_DISCARD 0x00000100 /* discard char with frame/par error */ +#define C_PR_IGNORE 0x00000200 /* ignore frame/par error */ + +/* comm_data_l - data length and stop bits */ + +#define C_DL_CS5 0x00000001 +#define C_DL_CS6 0x00000002 +#define C_DL_CS7 0x00000004 +#define C_DL_CS8 0x00000008 +#define C_DL_CS 0x0000000f +#define C_DL_1STOP 0x00000010 +#define C_DL_15STOP 0x00000020 +#define C_DL_2STOP 0x00000040 +#define C_DL_STOP 0x000000f0 + +/* interrupt enabling/status */ + +#define C_IN_DISABLE 0x00000000 /* zero, disable interrupts */ +#define C_IN_TXBEMPTY 0x00000001 /* tx buffer empty */ +#define C_IN_TXLOWWM 0x00000002 /* tx buffer below LWM */ +#define C_IN_RXHIWM 0x00000010 /* rx buffer above HWM */ +#define C_IN_RXNNDT 0x00000020 /* rx no new data timeout */ +#define C_IN_MDCD 0x00000100 /* modem DCD change */ +#define C_IN_MDSR 0x00000200 /* modem DSR change */ +#define C_IN_MRI 0x00000400 /* modem RI change */ +#define C_IN_MCTS 0x00000800 /* modem CTS change */ +#define C_IN_RXBRK 0x00001000 /* Break received */ +#define C_IN_PR_ERROR 0x00002000 /* parity error */ +#define C_IN_FR_ERROR 0x00004000 /* frame error */ +#define C_IN_OVR_ERROR 0x00008000 /* overrun error */ +#define C_IN_RXOFL 0x00010000 /* RX buffer overflow */ +#define C_IN_IOCTLW 0x00020000 /* I/O control w/ wait */ + +/* flow control */ + +#define C_FL_OXX 0x00000001 /* output Xon/Xoff flow control */ +#define C_FL_IXX 0x00000002 /* output Xon/Xoff flow control */ +#define C_FL_OIXANY 0x00000004 /* output Xon/Xoff (any xon) */ +#define C_FL_SWFLOW 0x0000000f + +/* flow status */ + +#define C_FS_TXIDLE 0x00000000 /* no Tx data in the buffer or UART */ +#define C_FS_SENDING 0x00000001 /* UART is sending data */ +#define C_FS_SWFLOW 0x00000002 /* Tx is stopped by received Xoff */ + +/* rs_control/rs_status RS-232 signals */ + +#define C_RS_DCD 0x00000100 /* CD */ +#define C_RS_DSR 0x00000200 /* DSR */ +#define C_RS_RI 0x00000400 /* RI */ +#define C_RS_CTS 0x00000800 /* CTS */ +#define C_RS_RTS 0x00000001 /* RTS */ +#define C_RS_DTR 0x00000004 /* DTR */ + +/* commands Host <-> Board */ + +#define C_CM_RESET 0x01 /* reset/flush buffers */ +#define C_CM_IOCTL 0x02 /* re-read CH_CTRL */ +#define C_CM_IOCTLW 0x03 /* re-read CH_CTRL, intr when done */ +#define C_CM_IOCTLM 0x04 /* RS-232 outputs change */ +#define C_CM_SENDXOFF 0x10 /* send Xoff */ +#define C_CM_SENDXON 0x11 /* send Xon */ +#define C_CM_CLFLOW 0x12 /* Clear flow control (resume) */ +#define C_CM_SENDBRK 0x41 /* send break */ +#define C_CM_INTBACK 0x42 /* Interrupt back */ +#define C_CM_SET_BREAK 0x43 /* Tx break on */ +#define C_CM_CLR_BREAK 0x44 /* Tx break off */ +#define C_CM_CMD_DONE 0x45 /* Previous command done */ +#define C_CM_TINACT 0x51 /* set inactivity detection */ +#define C_CM_IRQ_ENBL 0x52 /* enable generation of interrupts */ +#define C_CM_IRQ_DSBL 0x53 /* disable generation of interrupts */ +#define C_CM_ACK_ENBL 0x54 /* enable acknolowdged interrupt mode */ +#define C_CM_ACK_DSBL 0x55 /* disable acknolowdged intr mode */ +#define C_CM_FLUSH_RX 0x56 /* flushes Rx buffer */ +#define C_CM_FLUSH_TX 0x57 /* flushes Tx buffer */ + +#define C_CM_TXBEMPTY 0x60 /* Tx buffer is empty */ +#define C_CM_TXLOWWM 0x61 /* Tx buffer low water mark */ +#define C_CM_RXHIWM 0x62 /* Rx buffer high water mark */ +#define C_CM_RXNNDT 0x63 /* rx no new data timeout */ +#define C_CM_MDCD 0x70 /* modem DCD change */ +#define C_CM_MDSR 0x71 /* modem DSR change */ +#define C_CM_MRI 0x72 /* modem RI change */ +#define C_CM_MCTS 0x73 /* modem CTS change */ +#define C_CM_RXBRK 0x84 /* Break received */ +#define C_CM_PR_ERROR 0x85 /* Parity error */ +#define C_CM_FR_ERROR 0x86 /* Frame error */ +#define C_CM_OVR_ERROR 0x87 /* Overrun error */ +#define C_CM_RXOFL 0x88 /* RX buffer overflow */ +#define C_CM_CMDERROR 0x90 /* command error */ +#define C_CM_FATAL 0x91 /* fatal error */ +#define C_CM_HW_RESET 0x92 /* reset board */ + +/* + * CH_CTRL - This per port structure contains all parameters + * that control an specific port. It can be seen as the + * configuration registers of a "super-serial-controller". + */ + +struct CH_CTRL { + uclong op_mode; /* operation mode */ + uclong intr_enable; /* interrupt masking */ + uclong sw_flow; /* SW flow control */ + uclong flow_status; /* output flow status */ + uclong comm_baud; /* baud rate - numerically specified */ + uclong comm_parity; /* parity */ + uclong comm_data_l; /* data length/stop */ + uclong comm_flags; /* other flags */ + uclong hw_flow; /* HW flow control */ + uclong rs_control; /* RS-232 outputs */ + uclong rs_status; /* RS-232 inputs */ + uclong flow_xon; /* xon char */ + uclong flow_xoff; /* xoff char */ + uclong hw_overflow; /* hw overflow counter */ + uclong sw_overflow; /* sw overflow counter */ + uclong comm_error; /* frame/parity error counter */ +}; + + +/* + * BUF_CTRL - This per channel structure contains + * all Tx and Rx buffer control for a given channel. + */ + +struct BUF_CTRL { + uclong flag_dma; /* buffers are in Host memory */ + uclong tx_bufaddr; /* address of the tx buffer */ + uclong tx_bufsize; /* tx buffer size */ + uclong tx_threshold; /* tx low water mark */ + uclong tx_get; /* tail index tx buf */ + uclong tx_put; /* head index tx buf */ + uclong rx_bufaddr; /* address of the rx buffer */ + uclong rx_bufsize; /* rx buffer size */ + uclong rx_threshold; /* rx high water mark */ + uclong rx_get; /* tail index rx buf */ + uclong rx_put; /* head index rx buf */ + uclong filler[5]; /* filler to align structures */ +}; + +/* + * BOARD_CTRL - This per board structure contains all global + * control fields related to the board. + */ + +struct BOARD_CTRL { + + /* static info provided by the on-board CPU */ + uclong n_channel; /* number of channels */ + uclong fw_version; /* firmware version */ + + /* static info provided by the driver */ + uclong op_system; /* op_system id */ + uclong dr_version; /* driver version */ + + /* board control area */ + uclong inactivity; /* inactivity control */ + + /* host to FW commands */ + uclong hcmd_channel; /* channel number */ + uclong hcmd_param; /* pointer to parameters */ + + /* FW to Host commands */ + uclong fwcmd_channel; /* channel number */ + uclong fwcmd_param; /* pointer to parameters */ + + /* filler so the structures are aligned */ + uclong filler[7]; +}; + +/* + * ZFW_CTRL - This is the data structure that includes all other + * data structures used by the Firmware. + */ + +struct ZFW_CTRL { + struct BOARD_CTRL board_ctrl; + struct CH_CTRL ch_ctrl[MAX_CHAN]; + struct BUF_CTRL buf_ctrl[MAX_CHAN]; +}; + +/****************** ****************** *******************/ +#endif + + + #ifdef __KERNEL__ +/*************************************** + * Memory access functions/macros * + * (required to support Alpha systems) * + ***************************************/ + +#define cy_writeb(port,val) {writeb((ucchar)(val),(ulong)(port)); mb();} +#define cy_writew(port,val) {writew((ushort)(val),(ulong)(port)); mb();} +#define cy_writel(port,val) {writel((uclong)(val),(ulong)(port)); mb();} + +#define cy_readb(port) readb(port) +#define cy_readw(port) readw(port) +#define cy_readl(port) readl(port) + /* Per card data structure */ struct cyclades_card { - int base_addr; + long base_addr; + long ctl_addr; int irq; - int num_chips; /* 0 if card is absent */ + int num_chips; /* 0 if card absent, -1 if Z/PCI, else Y */ int first_line; /* minor number of first channel on card */ int bus_index; /* address shift - 0 for ISA, 1 for PCI */ + int inact_ctrl; /* FW Inactivity control - 0 disabled, 1 enabled */ }; struct cyclades_chip { @@ -62,23 +513,28 @@ struct cyclades_port { int magic; - int type; int card; int line; int flags; /* defined in tty.h */ + int type; /* UART type */ struct tty_struct *tty; int read_status_mask; + int ignore_status_mask; int timeout; int xmit_fifo_size; int cor1,cor2,cor3,cor4,cor5; int tbpr,tco,rbpr,rco; - int ignore_status_mask; + int baud; + int rflow; + int rtsdtr_inv; + int chip_rev; + int custom_divisor; + int x_char; /* to be pushed out ASAP */ int close_delay; - int IER; /* Interrupt Enable Register */ - int event; + unsigned short closing_wait; + unsigned long event; unsigned long last_active; int count; /* # of fd on device */ - int x_char; /* to be pushed out ASAP */ int x_break; int blocked_open; /* # of blocked opens */ long session; /* Session of opening process */ @@ -94,29 +550,50 @@ struct termios callout_termios; struct wait_queue *open_wait; struct wait_queue *close_wait; + struct wait_queue *shutdown_wait; struct cyclades_monitor mon; + unsigned long jiffies[3]; + unsigned long rflush_count; + struct cyclades_idle_stats idle_stats; }; /* * Events are used to schedule things to happen at timer-interrupt * time, instead of at cy interrupt time. */ -#define Cy_EVENT_READ_PROCESS 0 -#define Cy_EVENT_WRITE_WAKEUP 1 -#define Cy_EVENT_HANGUP 2 -#define Cy_EVENT_BREAK 3 -#define Cy_EVENT_OPEN_WAKEUP 4 - - - -#define CyMaxChipsPerCard 8 +#define Cy_EVENT_READ_PROCESS 0 +#define Cy_EVENT_WRITE_WAKEUP 1 +#define Cy_EVENT_HANGUP 2 +#define Cy_EVENT_BREAK 3 +#define Cy_EVENT_OPEN_WAKEUP 4 +#define Cy_EVENT_SHUTDOWN_WAKEUP 5 + +#define CLOSING_WAIT_DELAY 60*HZ +#define CY_CLOSING_WAIT_NONE 65535 +#define CY_CLOSING_WAIT_INF 0 + + +#define CyMAX_CHIPS_PER_CARD 8 +#define CyMAX_CHAR_FIFO 12 +#define CyPORTS_PER_CHIP 4 +#define CD1400_MAX_SPEED 115200 + +#define CyISA_Ywin 0x2000 + +#define CyPCI_Ywin 0x4000 +#define CyPCI_Zctl CTRL_WINDOW_SIZE +#define CyPCI_Zwin 0x80000 +#define CyPCI_Ze_win (2 * CyPCI_Zwin) /**** CD1400 registers ****/ -#define CyRegSize 0x0400 -#define Cy_HwReset 0x1400 -#define Cy_ClrIntr 0x1800 -#define Cy_EpldRev 0x1e00 +#define CD1400_REV_G 0x46 +#define CD1400_REV_J 0x48 + +#define CyRegSize 0x0400 +#define Cy_HwReset 0x1400 +#define Cy_ClrIntr 0x1800 +#define Cy_EpldRev 0x1e00 /* Global Registers */ @@ -152,6 +629,9 @@ #define CyPPR (0x7E*2) #define CyCLOCK_20_1MS (0x27) #define CyCLOCK_25_1MS (0x31) +#define CyCLOCK_25_5MS (0xf4) +#define CyCLOCK_60_1MS (0x75) +#define CyCLOCK_60_2MS (0xea) /* Virtual Registers */ @@ -274,9 +754,12 @@ #define CyTBPR (0x72*2) #define CyTCOR (0x76*2) -/* max number of chars in the FIFO */ +/* Custom Registers */ -#define CyMAX_CHAR_FIFO 12 +#define CyPLX_VER (0x3400) +#define PLX_9050 0x0b +#define PLX_9060 0x0c +#define PLX_9080 0x0d /***************************************************************************/ diff -u --recursive --new-file v2.0.35/linux/include/linux/fs.h linux/include/linux/fs.h --- v2.0.35/linux/include/linux/fs.h Sun Nov 15 10:49:52 1998 +++ linux/include/linux/fs.h Sun Nov 15 10:33:17 1998 @@ -75,6 +75,7 @@ #define S_IMMUTABLE 512 /* Immutable file */ #define MS_NOATIME 1024 /* Do not update access times. */ #define S_BAD_INODE 2048 /* Marker for unreadable inodes */ +#define S_ZERO_WR 4096 /* Device accepts 0 length writes */ /* * Flags that can be altered by MS_REMOUNT @@ -106,6 +107,7 @@ #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND) #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) #define IS_NOATIME(inode) ((inode)->i_flags & MS_NOATIME) +#define IS_ZERO_WR(inode) ((inode)->i_flags & S_ZERO_WR) #define UPDATE_ATIME(inode) \ if (!IS_NOATIME(inode) && !IS_RDONLY(inode)) { \ @@ -552,6 +554,7 @@ asmlinkage int sys_open(const char *, int, int); asmlinkage int sys_close(unsigned int); /* yes, it's really unsigned */ +asmlinkage int sys_read(unsigned int, char *, int); extern void kill_fasync(struct fasync_struct *fa, int sig); diff -u --recursive --new-file v2.0.35/linux/include/linux/genhd.h linux/include/linux/genhd.h --- v2.0.35/linux/include/linux/genhd.h Mon Jul 13 13:46:41 1998 +++ linux/include/linux/genhd.h Sun Nov 15 10:33:17 1998 @@ -43,7 +43,8 @@ unsigned char end_cyl; /* end cylinder */ unsigned int start_sect; /* starting sector counting from 0 */ unsigned int nr_sects; /* nr of sectors in partition */ -}; +} __attribute((packed)); /* Give a polite hint to egcs/alpha to generate + unaligned operations */ struct hd_struct { long start_sect; diff -u --recursive --new-file v2.0.35/linux/include/linux/if_shaper.h linux/include/linux/if_shaper.h --- v2.0.35/linux/include/linux/if_shaper.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/if_shaper.h Sun Nov 15 10:33:17 1998 @@ -0,0 +1,62 @@ +#ifndef __LINUX_SHAPER_H +#define __LINUX_SHAPER_H + +#ifdef __KERNEL__ + +#define SHAPER_QLEN 10 +/* + * This is a bit speed dependant (read it shouldnt be a constant!) + * + * 5 is about right for 28.8 upwards. Below that double for every + * halving of speed or so. - ie about 20 for 9600 baud. + */ +#define SHAPER_LATENCY (5*HZ) +#define SHAPER_MAXSLIP 2 +#define SHAPER_BURST (HZ/50) /* Good for >128K then */ + +struct shaper +{ + struct sk_buff_head sendq; + __u32 bitspersec; + __u32 bytespertick; + __u32 shapelatency; + __u32 shapeclock; + __u32 recovery; /* Time we can next clock a packet out on + an empty queue */ + char locked; + struct device *dev; + int (*hard_start_xmit) (struct sk_buff *skb, + struct device *dev); + int (*hard_header) (struct sk_buff *skb, + struct device *dev, + unsigned short type, + void *daddr, + void *saddr, + unsigned len); + int (*rebuild_header)(void *eth, struct device *dev, + unsigned long raddr, struct sk_buff *skb); + struct enet_statistics* (*get_stats)(struct device *dev); + struct wait_queue *wait_queue; + struct timer_list timer; +}; + +#endif + +#define SHAPER_SET_DEV 0x0001 +#define SHAPER_SET_SPEED 0x0002 +#define SHAPER_GET_DEV 0x0003 +#define SHAPER_GET_SPEED 0x0004 + +struct shaperconf +{ + __u16 ss_cmd; + union + { + char ssu_name[14]; + __u32 ssu_speed; + } ss_u; +#define ss_speed ss_u.ssu_speed +#define ss_name ss_u.ssu_name +}; + +#endif diff -u --recursive --new-file v2.0.35/linux/include/linux/if_wic.h linux/include/linux/if_wic.h --- v2.0.35/linux/include/linux/if_wic.h Wed Oct 15 15:11:27 1997 +++ linux/include/linux/if_wic.h Wed Dec 31 16:00:00 1969 @@ -1,102 +0,0 @@ -#ifndef _LINUX_IF_WIC_H -#define _LINUX_IF_WIC_H - -#include - -#define SIOCDEVWIC SIOCDEVPRIVATE - -struct wicconf -{ - unsigned char pcmd; - unsigned char data[120]; - unsigned char len; -}; - -/* WIC host to controller commands */ - -#define WIC_AYT 0x10 /* test dki */ -#define WIC_RESET 0x11 /* reset controller */ -#define WIC_SETSN 0x21 /* set station name */ -#define WIC_SETPS 0x22 /* set power saving mode */ -#define WIC_SETAF 0x23 /* set announce filter */ -#define WIC_SETGPF 0x24 /* set GPSP filter */ -#define WIC_GETVERH 0x61 /* get interface controller version */ -#define WIC_GETNL 0x62 /* get neighbor list */ -#define WIC_GETSN 0x65 /* get station name */ -#define WIC_CLRSTATS 0x83 /* clear controller statistics */ -#define WIC_SETNET 0x84 /* set network configuration */ -#define WIC_SETSYS 0x85 /* set system configuration */ -#define WIC_GETSTATS 0xc1 /* get statistics */ -#define WIC_GETVERM 0xc3 /* get MAC version */ -#define WIC_GETNET 0xc4 /* get network configuration */ -#define WIC_GETSYS 0xc5 /* get system configuration */ - -/* - * structure used for the GETNET/SETNET command - */ - -struct wic_net { - unsigned char ula[6]; /* ula of interface */ - unsigned char mode; /* operating mode */ -#define NET_MODE_ME 0x01 /* receive my ula */ -#define NET_MODE_BCAST 0x02 /* receive bcasts */ -#define NET_MODE_MCAST 0x04 /* receive mcasts */ -#define NET_MODE_PROM 0x08 /* promiscuous */ -#define NET_MODE_HC 0x10 /* is a hop coordinator */ -#define NET_MODE_HC_VALID 0x20 /* hc address is valid */ -#define NET_MODE_HCAP 0x40 /* hc is also ap */ -#define NET_MODE_HC_KNOWN 0x80 /* hc is known */ - unsigned char rts_lo; /* rts threshold */ - unsigned char rts_hi; /* rts threshold */ - unsigned char retry; /* retry limit */ - unsigned char hc_ula[6]; /* ula of hc */ - unsigned char key[4]; /* network key */ - unsigned char dsl; /* direct send limit */ - unsigned char res1; /* reserved */ -}; - -/* - * structure used for the GETSYS/SETSYS command - */ - -struct wic_sys { - unsigned char mode; /* set operating mode */ -#define SYS_MODE_ANT_DIV 0x00 /* use antenna diversity */ -#define SYS_MODE_ANT_1 0x01 /* use ant 1 for tx */ -#define SYS_MODE_ANT_2 0x02 /* use ant 2 for tx */ -#define SYS_MODE_HC_LOCK 0x04 /* lock onto current hc */ -#define SYS_MODE_DEBUG 0x08 /* upload failed frames */ -#define SYS_MODE_IAM_AP 0x10 /* I am AP */ -#define SYS_MODE_IAM_HC 0x20 /* I am HC */ -#define SYS_MODE_USE_SKIP 0x40 /* use skipping mechanism */ -#define SYS_MODE_AUTO 0x80 /* station is in auto mode */ - unsigned char switches; /* radio/controller switches */ -#define SYS_SWITCH_STDBY 0x01 /* switch radio to standby */ -#define SYS_SWITCH_TXRX 0x02 /* 1 = tx, manual mode only */ -#define SYS_SWITCH_PA 0x04 /* 1 = enable PA on radio */ -#define SYS_SWITCH_PWR 0x10 /* 1 = hi, 0 = lo power output */ -#define SYS_SWITCH_RES1 0x20 /* reserved, must be 0 */ -#define SYS_SWITCH_LIGHTS 0x40 /* light for tx & rx */ -#define SYS_SWITCH_LIGHTS_HC 0x80 /* light for rx while coordinated */ - unsigned char hop_min; /* hop range */ - unsigned char hop_max; /* hop range */ - unsigned char pre_len; /* preamble length (bytes) */ - unsigned char pre_match; /* valid preamble match (bytes) */ - unsigned char mod; /* data mod: 1 = 8:1, 0 = none */ - unsigned char cca_mode; /* cca flags */ -#define CCA_PKT_DET_BSY 0x01 /* busy if packet is detected */ -#define CCA_VIRT_CARR 0x02 /* use virtual carrier */ -#define CCA_RSSI_BSY 0x04 /* busy if rssi > threshold */ -#define CCA_DATA_BSY 0x08 /* busy if valid data > XXX usec */ - unsigned char dwell_hi; /* dwell time */ - unsigned char dwell_lo; /* dwell time */ - unsigned char hc_timeout; /* HC timeout */ - unsigned char rssi; /* rssi threshold */ - unsigned char hc_rssi; /* rssi of last hc frame */ - unsigned char hc_rssi_chan; /* channel of hc rssi value */ -}; - - -#endif /* _LINUX_IF_WIC_H */ - - diff -u --recursive --new-file v2.0.35/linux/include/linux/interrupt.h linux/include/linux/interrupt.h --- v2.0.35/linux/include/linux/interrupt.h Thu Aug 14 13:30:58 1997 +++ linux/include/linux/interrupt.h Sun Nov 15 10:33:17 1998 @@ -39,7 +39,8 @@ IMMEDIATE_BH, KEYBOARD_BH, CYCLADES_BH, - CM206_BH + CM206_BH, + ISICOM_BH }; extern inline void init_bh(int nr, void (*routine)(void)) diff -u --recursive --new-file v2.0.35/linux/include/linux/ip.h linux/include/linux/ip.h --- v2.0.35/linux/include/linux/ip.h Wed Oct 15 15:23:10 1997 +++ linux/include/linux/ip.h Sun Nov 15 10:33:17 1998 @@ -65,7 +65,7 @@ #define IPOPT_TS_TSONLY 0 /* timestamps only */ #define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ -#define IPOPT_TS_PRESPEC 2 /* specified modules only */ +#define IPOPT_TS_PRESPEC 3 /* specified modules only */ struct options { __u32 faddr; /* Saved first hop address */ diff -u --recursive --new-file v2.0.35/linux/include/linux/isdn.h linux/include/linux/isdn.h --- v2.0.35/linux/include/linux/isdn.h Wed Oct 15 15:25:22 1997 +++ linux/include/linux/isdn.h Sun Nov 15 10:33:17 1998 @@ -1,8 +1,8 @@ -/* $Id: isdn.h,v 1.32 1997/08/21 09:49:46 fritz Exp $ +/* $Id: isdn.h,v 1.31.2.17 1998/11/05 22:13:28 fritz Exp $ * * Main header for the Linux ISDN subsystem (linklevel). * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) * @@ -21,8 +21,72 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdn.h,v $ - * Revision 1.32 1997/08/21 09:49:46 fritz - * Increased NET_DV + * Revision 1.31.2.17 1998/11/05 22:13:28 fritz + * Changed mail-address. + * + * Revision 1.31.2.16 1998/11/04 17:22:53 fritz + * Replaced broken lowlevel-driver locking. + * + * Revision 1.31.2.15 1998/11/03 14:31:57 fritz + * Reduced stack usage in various functions. + * Adapted statemachine to work with certified HiSax. + * Some fixes in callback handling. + * + * Revision 1.31.2.14 1998/10/25 14:37:37 fritz + * Backported from MIPS (Cobalt). + * + * Revision 1.31.2.13 1998/10/23 10:14:25 paul + * Implementation of "dialmode" (successor of "status") + * You also need current isdnctrl for this! + * + * Revision 1.31.2.12 1998/08/22 16:41:25 armin + * Added silence detection in audio receive mode (AT+VSD). + * + * Revision 1.31.2.11 1998/07/15 15:04:19 calle + * make isdn4k-utils compile again. + * + * Revision 1.31.2.10 1998/06/07 13:48:30 fritz + * ABC cleanup + * + * Revision 1.31.2.9 1998/06/02 12:12:49 detabc + * wegen einer einstweiliger verfuegung gegen DW ist zur zeit + * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar + * + * Revision 1.31.2.8 1998/05/06 08:30:44 detabc + * add Item to stop icmp-unreach (max. 6 times of dialwait delay) + * + * Revision 1.31.2.7 1998/04/26 19:51:40 detabc + * removed unused code + * + * Revision 1.31.2.6 1998/04/26 11:10:46 detabc + * add items for the abc_tx_queus and the abc_delayed_hangup + * only used if the abc-extension is enabled + * + * Revision 1.31.2.5 1998/04/18 17:39:45 detabc + * remove some unused abc-lines + * added defines und items for abc-secure callback (only used with abc-extenrsion) + * + * Revision 1.31.2.4 1998/04/08 21:42:25 keil + * Blocksize default 1024 + * + * Revision 1.31.2.3 1998/03/16 09:56:28 cal + * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches. + * + * Revision 1.31.2.2 1998/03/07 23:35:45 detabc + * added the abc-extension to the linux isdn-kernel + * for kernel-version 2.0.xx + * DO NOT USE FOR HIGHER KERNELS-VERSIONS + * all source-lines are switched with the define CONFIG_ISDN_WITH_ABC + * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes + * + * you need also a modified isdnctrl-source the switch on the + * features of the abc-extension + * + * please use carefully. more detail will be follow. + * thanks + * + * Revision 1.31.2.1 1997/08/21 15:57:04 fritz + * Synchronized 2.0.X branch with 2.0.31-pre7 * * Revision 1.31 1997/06/22 11:57:07 fritz * Added ability to adjust slave triggerlevel. @@ -137,6 +201,7 @@ #ifndef isdn_h #define isdn_h +#include #include #define ISDN_TTY_MAJOR 43 @@ -148,14 +213,20 @@ * the correspondent code in isdn.c */ +#ifdef CONFIG_COBALT_MICRO_SERVER +/* Save memory */ +#define ISDN_MAX_DRIVERS 2 +#define ISDN_MAX_CHANNELS 8 +#else #define ISDN_MAX_DRIVERS 32 #define ISDN_MAX_CHANNELS 64 +#endif #define ISDN_MINOR_B 0 #define ISDN_MINOR_BMAX (ISDN_MAX_CHANNELS-1) -#define ISDN_MINOR_CTRL ISDN_MAX_CHANNELS -#define ISDN_MINOR_CTRLMAX (2*ISDN_MAX_CHANNELS-1) -#define ISDN_MINOR_PPP (2*ISDN_MAX_CHANNELS) -#define ISDN_MINOR_PPPMAX (3*ISDN_MAX_CHANNELS-1) +#define ISDN_MINOR_CTRL 64 +#define ISDN_MINOR_CTRLMAX (64 + (ISDN_MAX_CHANNELS-1)) +#define ISDN_MINOR_PPP 128 +#define ISDN_MINOR_PPPMAX (128 + (ISDN_MAX_CHANNELS-1)) #define ISDN_MINOR_STATUS 255 /* New ioctl-codes */ @@ -166,8 +237,8 @@ #define IIOCNETANM _IO('I',5) #define IIOCNETDNM _IO('I',6) #define IIOCNETGNM _IO('I',7) -#define IIOCGETSET _IO('I',8) -#define IIOCSETSET _IO('I',9) +/* #define IIOCGETSET _IO('I',8) obsolete */ +/* #define IIOCSETSET _IO('I',9) obsolete */ #define IIOCSETVER _IO('I',10) #define IIOCNETHUP _IO('I',11) #define IIOCSETGST _IO('I',12) @@ -210,6 +281,7 @@ #define ISDN_MODEM_ANZREG 23 /* Number of Modem-Registers */ #define ISDN_MSNLEN 20 +#define ISDN_LMSNLEN 255 /* Length of tty's Listen-MSN string */ typedef struct { char drvid[25]; @@ -230,6 +302,7 @@ #define NET_DV 0x04 /* Data version for net_cfg */ #define TTY_DV 0x04 /* Data version for iprofd etc. */ +#define INF_DV 0x01 /* Data version for /dev/isdninfo */ typedef struct { char name[10]; /* Name of interface */ @@ -254,8 +327,17 @@ int pppbind; /* ippp device for bindings */ int chargeint; /* Use fixed charge interval length */ int triggercps; /* BogoCPS needed for triggering slave */ + int dialtimeout; /* Dial-Timeout */ + int dialwait; /* Time to wait after failed dial */ + int dialmode; /* Flag: off / on / auto */ } isdn_net_ioctl_cfg; +#define ISDN_NET_DIALMODE_MASK 0xC0 /* bits for status */ +#define ISDN_NET_DM_OFF 0x00 /* this interface is stopped */ +#define ISDN_NET_DM_MANUAL 0x40 /* this interface is on (manual) */ +#define ISDN_NET_DM_AUTO 0x80 /* this interface is autodial */ +#define ISDN_NET_DIALMODE(x) ((&(x))->flags & ISDN_NET_DIALMODE_MASK) + #ifdef __KERNEL__ #ifndef STANDALONE @@ -346,6 +428,7 @@ /* Timeout-Values for isdn_net_dial() */ #define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1))) #define ISDN_TIMER_DTIMEOUT15 (15*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1))) +#define ISDN_TIMER_DTIMEOUT60 (60*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1))) /* GLOBAL_FLAGS */ #define ISDN_GLOBAL_STOPPED 1 @@ -364,6 +447,12 @@ #define ISDN_NET_TMP 0x10 /* tmp interface until getting an IP */ #define ISDN_NET_DYNAMIC 0x20 /* this link is dynamically allocated */ #endif + +/* + * also see the ISDN_NET_DM_* defines earlier + * (they are not here, as we need access to those in userspace) + */ + #define ISDN_NET_MAGIC 0x49344C02 /* for paranoia-checking */ /* Phone-list-element */ @@ -372,6 +461,7 @@ char num[ISDN_MSNLEN]; } isdn_net_phone; + /* Local interface-data */ typedef struct isdn_net_local_s { ulong magic; @@ -423,7 +513,7 @@ int sqfull; /* Flag: netdev-queue overloaded */ ulong sqfull_stamp; /* Start-Time of overload */ ulong slavedelay; /* Dynamic bundling delaytime */ - int triggercps; /* BogoCPS needed for trigger slave */ + int triggercps; /* BogoCPS needed for triggering slave */ struct device *srobin; /* Ptr to Master device for slaves */ isdn_net_phone *phone[2]; /* List of remote-phonenumbers */ /* phone[0] = Incoming Numbers */ @@ -460,6 +550,11 @@ struct device *, unsigned char *); int pppbind; /* ippp device for bindings */ + int dialtimeout; /* How long shall we try on dialing? (jiffies) */ + int dialwait; /* How long shall we wait after failed attempt? (jiffies) */ + ulong dialstarted; /* jiffies of first dialing-attempt */ + ulong dialwait_timer; /* jiffies of earliest next dialing-attempt */ + int huptimeout; /* How long will the connection be up? (seconds) */ } isdn_net_local; #ifdef CONFIG_ISDN_PPP @@ -502,7 +597,8 @@ #define ISDN_ASYNC_PGRP_LOCKOUT 0x0200 /* Lock cua opens on pgrp */ #define ISDN_ASYNC_CALLOUT_NOHUP 0x0400 /* No hangup for cui */ #define ISDN_ASYNC_SPLIT_TERMIOS 0x0008 /* Sep. termios for dialin/out */ -#define ISDN_SERIAL_XMIT_SIZE 4000 /* Maximum bufsize for write */ +#define ISDN_SERIAL_XMIT_SIZE 1024 /* default bufsize for write */ +#define ISDN_SERIAL_XMIT_MAX 4000 /* Maximum bufsize for write */ #define ISDN_SERIAL_TYPE_NORMAL 1 #define ISDN_SERIAL_TYPE_CALLOUT 2 @@ -581,6 +677,7 @@ void *adpcms; /* state for adpcm decompression */ void *adpcmr; /* state for adpcm compression */ void *dtmf_state; /* state for dtmf decoder */ + void *silence_state; /* state for silence detection */ #endif struct tty_struct *tty; /* Pointer to corresponding tty */ atemu emu; /* AT-emulator data */ @@ -624,7 +721,7 @@ struct mpqueue { struct mpqueue *next; struct mpqueue *last; - long sqno; + long sqno; struct sk_buff *skb; int BEbyte; unsigned long time; @@ -663,8 +760,12 @@ struct slcompress *slcomp; #endif unsigned long debug; - struct isdn_ppp_compressor *compressor,*link_compressor; + struct isdn_ppp_compressor *compressor, *link_compressor; void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat; +#ifdef ISDN_SYNCPPP_READDRESS + unsigned long old_pa_addr; + unsigned long old_pa_dstaddr; +#endif }; #endif @@ -678,16 +779,19 @@ char *private; } infostruct; +#define DRV_FLAG_RUNNING 1 +#define DRV_FLAG_REJBUS 2 +#define DRV_FLAG_LOADED 4 + /* Description of hardware-level-driver */ typedef struct { - ulong flags; /* Flags */ + ulong online; /* Channel-Online flags */ + ulong flags; /* Misc driver flags */ + int locks; /* Number of locks for this driver */ int channels; /* Number of channels */ - int reject_bus; /* Flag: Reject rejected call on bus*/ struct wait_queue *st_waitq; /* Wait-Queue for status-read's */ int maxbufsize; /* Maximum Buffersize supported */ unsigned long pktcount; /* Until now: unused */ - int running; /* Flag: Protocolcode running */ - int loaded; /* Flag: Driver loaded */ int stavail; /* Chars avail on Status-device */ isdn_if *interface; /* Interface to driver */ int *rcverr; /* Error-counters for B-Ch.-receive */ diff -u --recursive --new-file v2.0.35/linux/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h --- v2.0.35/linux/include/linux/isdn_ppp.h Mon Aug 4 17:34:01 1997 +++ linux/include/linux/isdn_ppp.h Sun Nov 15 10:33:17 1998 @@ -8,6 +8,8 @@ #define CALLTYPE_OUTGOING 0x2 #define CALLTYPE_CALLBACK 0x4 +#define IPPP_VERSION "2.2.0" + struct pppcallinfo { int calltype; diff -u --recursive --new-file v2.0.35/linux/include/linux/isdnif.h linux/include/linux/isdnif.h --- v2.0.35/linux/include/linux/isdnif.h Wed Oct 15 15:25:02 1997 +++ linux/include/linux/isdnif.h Sun Nov 15 10:33:17 1998 @@ -1,10 +1,10 @@ -/* $Id: isdnif.h,v 1.20 1997/05/27 15:18:06 fritz Exp $ +/* $Id: isdnif.h,v 1.20.2.2 1998/11/05 22:13:33 fritz Exp $ * * Linux ISDN subsystem * * Definition of the interface between the subsystem and its low-level drivers. * - * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: isdnif.h,v $ + * Revision 1.20.2.2 1998/11/05 22:13:33 fritz + * Changed mail-address. + * + * Revision 1.20.2.1 1998/03/07 23:00:50 tsbogend + * added defines for Linux/Alpha 2.0.x with alpha-patches + * * Revision 1.20 1997/05/27 15:18:06 fritz * Added changes for recent 2.1.x kernels: * changed return type of isdn_close @@ -382,9 +388,15 @@ } #define GET_USER(x, addr) ( x = get_user(addr) ) +#ifdef __alpha__ /* needed for 2.0.x with alpha-patches */ +#define RWTYPE long +#define LSTYPE long +#define RWARG unsigned long +#else #define RWTYPE int #define LSTYPE int #define RWARG int +#endif #define LSARG off_t #else #include diff -u --recursive --new-file v2.0.35/linux/include/linux/isicom.h linux/include/linux/isicom.h --- v2.0.35/linux/include/linux/isicom.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/isicom.h Sun Nov 15 10:33:17 1998 @@ -0,0 +1,300 @@ +#ifndef _LINUX_ISICOM_H +#define _LINUX_ISICOM_H + +/*#define ISICOM_DEBUG*/ +/*#define ISICOM_DEBUG_DTR_RTS*/ + + +/* + * Firmware Loader definitions ... + */ + +#define __MultiTech ('M'<<8) +#define MIOCTL_LOAD_FIRMWARE (__MultiTech | 0x01) +#define MIOCTL_READ_FIRMWARE (__MultiTech | 0x02) +#define MIOCTL_XFER_CTRL (__MultiTech | 0x03) +#define MIOCTL_RESET_CARD (__MultiTech | 0x04) + +#define DATA_SIZE 16 + +typedef struct { + unsigned short exec_segment; + unsigned short exec_addr; +} exec_record; + +typedef struct { + int board; /* Board to load */ + unsigned short addr; + unsigned short count; +} bin_header; + +typedef struct { + int board; /* Board to load */ + unsigned short addr; + unsigned short count; + unsigned short segment; + unsigned char bin_data[DATA_SIZE]; +} bin_frame; + +#ifdef __KERNEL__ + +#define YES 1 +#define NO 0 + +#define ISILOAD_MISC_MINOR 155 /* /dev/isctl */ +#define ISILOAD_NAME "ISILoad" + +/* + * ISICOM Driver definitions ... + * + */ + +#define ISICOM_NAME "ISICom" + +/* + * These are now officially allocated numbers + */ + +#define ISICOM_NMAJOR 112 /* normal */ +#define ISICOM_CMAJOR 113 /* callout */ +#define ISICOM_MAGIC (('M' << 8) | 'T') + +#define WAKEUP_CHARS 256 /* hard coded for now */ +#define TX_SIZE 254 + +#define BOARD_COUNT 4 +#define PORT_COUNT (BOARD_COUNT*16) + +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +/* character sizes */ + +#define ISICOM_CS5 0x0000 +#define ISICOM_CS6 0x0001 +#define ISICOM_CS7 0x0002 +#define ISICOM_CS8 0x0003 + +/* stop bits */ + +#define ISICOM_1SB 0x0000 +#define ISICOM_2SB 0x0004 + +/* parity */ + +#define ISICOM_NOPAR 0x0000 +#define ISICOM_ODPAR 0x0008 +#define ISICOM_EVPAR 0x0018 + +/* flow control */ + +#define ISICOM_CTSRTS 0x03 +#define ISICOM_INITIATE_XONXOFF 0x04 +#define ISICOM_RESPOND_XONXOFF 0x08 + +#define InterruptTheCard(base) (outw(0,(base)+0xc)) +#define ClearInterrupt(base) (inw((base)+0x0a)) + +#define BOARD(line) (((line) >> 4) & 0x3) +#define MIN(a, b) ( (a) < (b) ? (a) : (b) ) + + /* isi kill queue bitmap */ + +#define ISICOM_KILLTX 0x01 +#define ISICOM_KILLRX 0x02 + + /* isi_board status bitmap */ + +#define FIRMWARE_LOADED 0x0001 +#define BOARD_ACTIVE 0x0002 + + /* isi_port status bitmap */ + +#define ISI_CTS 0x1000 +#define ISI_DSR 0x2000 +#define ISI_RI 0x4000 +#define ISI_DCD 0x8000 +#define ISI_DTR 0x0100 +#define ISI_RTS 0x0200 + + +#define ISI_TXOK 0x0001 + +struct isi_board { + unsigned short base; + unsigned char irq; + unsigned char port_count; + unsigned short status; + unsigned short port_status; /* each bit represents a single port */ + unsigned short shift_count; + struct isi_port * ports; + signed char count; +}; + +struct isi_port { + unsigned short magic; + unsigned int flags; + int count; + int blocked_open; + int close_delay; + unsigned short channel; + unsigned short status; + unsigned short closing_wait; + long session; + long pgrp; + struct isi_board * card; + struct tty_struct * tty; + struct wait_queue * close_wait; + struct wait_queue * open_wait; + struct tq_struct hangup_tq; + struct tq_struct bh_tqueue; + unsigned char * xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct termios normal_termios; + struct termios callout_termios; +}; + + +/* + * ISI Card specific ops ... + */ + +extern inline void raise_dtr(struct isi_port * port) +{ + struct isi_board * card = port->card; + unsigned short base = card->base; + unsigned char channel = port->channel; + short wait=300; + while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); + if (wait <= 0) { + printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr.\n"); + return; + } +#ifdef ISICOM_DEBUG_DTR_RTS + printk(KERN_DEBUG "ISICOM: raise_dtr.\n"); +#endif + outw(0x8000 | (channel << card->shift_count) | 0x02 , base); + outw(0x0504, base); + InterruptTheCard(base); + port->status |= ISI_DTR; +} +extern inline void drop_dtr(struct isi_port * port) +{ + struct isi_board * card = port->card; + unsigned short base = card->base; + unsigned char channel = port->channel; + short wait=300; + while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); + if (wait <= 0) { + printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr.\n"); + return; + } +#ifdef ISICOM_DEBUG_DTR_RTS + printk(KERN_DEBUG "ISICOM: drop_dtr.\n"); +#endif + outw(0x8000 | (channel << card->shift_count) | 0x02 , base); + outw(0x0404, base); + InterruptTheCard(base); + port->status &= ~ISI_DTR; +} +extern inline void raise_rts(struct isi_port * port) +{ + struct isi_board * card = port->card; + unsigned short base = card->base; + unsigned char channel = port->channel; + short wait=300; + while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); + if (wait <= 0) { + printk(KERN_WARNING "ISICOM: Card found busy in raise_rts.\n"); + return; + } +#ifdef ISICOM_DEBUG_DTR_RTS + printk(KERN_DEBUG "ISICOM: raise_rts.\n"); +#endif + outw(0x8000 | (channel << card->shift_count) | 0x02 , base); + outw(0x0a04, base); + InterruptTheCard(base); + port->status |= ISI_RTS; +} +extern inline void drop_rts(struct isi_port * port) +{ + struct isi_board * card = port->card; + unsigned short base = card->base; + unsigned char channel = port->channel; + short wait=300; + while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); + if (wait <= 0) { + printk(KERN_WARNING "ISICOM: Card found busy in drop_rts.\n"); + return; + } +#ifdef ISICOM_DEBUG_DTR_RTS + printk(KERN_DEBUG "ISICOM: drop_rts.\n"); +#endif + outw(0x8000 | (channel << card->shift_count) | 0x02 , base); + outw(0x0804, base); + InterruptTheCard(base); + port->status &= ~ISI_RTS; +} +extern inline void raise_dtr_rts(struct isi_port * port) +{ + struct isi_board * card = port->card; + unsigned short base = card->base; + unsigned char channel = port->channel; + short wait=300; + while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); + if (wait <= 0) { + printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr_rts.\n"); + return; + } +#ifdef ISICOM_DEBUG_DTR_RTS + printk(KERN_DEBUG "ISICOM: raise_dtr_rts.\n"); +#endif + outw(0x8000 | (channel << card->shift_count) | 0x02 , base); + outw(0x0f04, base); + InterruptTheCard(base); + port->status |= (ISI_DTR | ISI_RTS); +} +extern inline void drop_dtr_rts(struct isi_port * port) +{ + struct isi_board * card = port->card; + unsigned short base = card->base; + unsigned char channel = port->channel; + short wait=300; + while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); + if (wait <= 0) { + printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr_rts.\n"); + return; + } +#ifdef ISICOM_DEBUG_DTR_RTS + printk(KERN_DEBUG "ISICOM: drop_dtr_rts.\n"); +#endif + outw(0x8000 | (channel << card->shift_count) | 0x02 , base); + outw(0x0c04, base); + InterruptTheCard(base); + port->status &= ~(ISI_RTS | ISI_DTR); +} + +extern inline void kill_queue(struct isi_port * port, short queue) +{ + struct isi_board * card = port->card; + unsigned short base = card->base; + unsigned char channel = port->channel; + short wait=300; + while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); + if (wait <= 0) { + printk(KERN_WARNING "ISICOM: Card found busy in kill_queue.\n"); + return; + } +#ifdef ISICOM_DEBUG + printk(KERN_DEBUG "ISICOM: kill_queue 0x%x.\n", queue); +#endif + outw(0x8000 | (channel << card->shift_count) | 0x02 , base); + outw((queue << 8) | 0x06, base); + InterruptTheCard(base); +} + +#endif /* __KERNEL__ */ + +#endif /* ISICOM_H */ \ No newline at end of file diff -u --recursive --new-file v2.0.35/linux/include/linux/kerneld.h linux/include/linux/kerneld.h --- v2.0.35/linux/include/linux/kerneld.h Sat May 11 00:35:04 1996 +++ linux/include/linux/kerneld.h Sun Nov 15 10:33:17 1998 @@ -1,6 +1,8 @@ #ifndef _LINUX_KERNELD_H #define _LINUX_KERNELD_H +#include + #define KERNELD_SYSTEM 1 #define KERNELD_REQUEST_MODULE 2 /* "insmod" */ #define KERNELD_RELEASE_MODULE 3 /* "rmmod" */ diff -u --recursive --new-file v2.0.35/linux/include/linux/lp.h linux/include/linux/lp.h --- v2.0.35/linux/include/linux/lp.h Thu Nov 30 04:03:38 1995 +++ linux/include/linux/lp.h Sun Nov 15 10:33:17 1998 @@ -20,6 +20,7 @@ #define LP_ABORT 0x0040 #define LP_CAREFUL 0x0080 #define LP_ABORTOPEN 0x0100 +#define LP_STRICT 0x0200 /* timeout for each character. This is relative to bus cycles -- it * is the count in a busy loop. THIS IS THE VALUE TO CHANGE if you @@ -70,6 +71,7 @@ #define LPRESET 0x060c /* reset printer */ #define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */ #define LPGETFLAGS 0x060e /* get status flags */ +#define LPSTRICT 0x060f /* enable/disable strict compliance */ /* timeout for printk'ing a timeout, in jiffies (100ths of a second). This is also used for re-checking error conditions if LP_ABORT is diff -u --recursive --new-file v2.0.35/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h --- v2.0.35/linux/include/linux/msdos_fs.h Mon Jul 13 13:46:42 1998 +++ linux/include/linux/msdos_fs.h Sun Nov 15 10:33:17 1998 @@ -7,7 +7,6 @@ #include #include #include -#include #include #define MSDOS_ROOT_INO 1 /* == MINIX_ROOT_INO */ diff -u --recursive --new-file v2.0.35/linux/include/linux/pci.h linux/include/linux/pci.h --- v2.0.35/linux/include/linux/pci.h Mon Jul 13 13:46:42 1998 +++ linux/include/linux/pci.h Sun Nov 15 10:33:18 1998 @@ -546,8 +546,11 @@ #define PCI_DEVICE_ID_DATABOOK_87144 0xb106 #define PCI_VENDOR_ID_PLX 0x10b5 +#define PCI_DEVICE_ID_PLX_9050 0x9050 #define PCI_DEVICE_ID_PLX_9080 0x9080 +#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103 + #define PCI_VENDOR_ID_MADGE 0x10b6 #define PCI_DEVICE_ID_MADGE_MK2 0x0002 @@ -765,6 +768,7 @@ #define PCI_DEVICE_ID_ATT_L56XMF 0x0440 #define PCI_VENDOR_ID_SPECIALIX 0x11cb +#define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000 #define PCI_DEVICE_ID_SPECIALIX_XIO 0x4000 #define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 @@ -827,6 +831,9 @@ #define PCI_DEVICE_ID_OPTIBASE_VPLEXCC 0x2120 #define PCI_DEVICE_ID_OPTIBASE_VQUEST 0x2130 +#define PCI_VENDOR_ID_ASIX 0x125b +#define PCI_DEVICE_ID_ASIX_88140 0x1400 + #define PCI_VENDOR_ID_SATSAGEM 0x1267 #define PCI_DEVICE_ID_SATSAGEM_PCR2101 0x5352 #define PCI_DEVICE_ID_SATSAGEM_TELSATTURBO 0x5a4b @@ -920,6 +927,9 @@ #define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 #define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 #define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 +#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71A0 +#define PCI_DEVICE_ID_INTEL_82443GX_1 0x71A1 +#define PCI_DEVICE_ID_INTEL_82443GX_2 0x71A2 #define PCI_DEVICE_ID_INTEL_P6 0x84c4 #define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 @@ -945,6 +955,12 @@ #define PCI_DEVICE_ID_ADAPTEC_7883 0x8378 #define PCI_DEVICE_ID_ADAPTEC_7884 0x8478 #define PCI_DEVICE_ID_ADAPTEC_1030 0x8b78 + +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 +#define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010 +#define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f +#define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050 +#define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f #define PCI_VENDOR_ID_ATRONICS 0x907f #define PCI_DEVICE_ID_ATRONICS_2015 0x2015 diff -u --recursive --new-file v2.0.35/linux/include/linux/personality.h linux/include/linux/personality.h --- v2.0.35/linux/include/linux/personality.h Mon Jul 13 13:46:42 1998 +++ linux/include/linux/personality.h Sun Nov 15 10:33:18 1998 @@ -27,7 +27,7 @@ #define PER_XENIX (0x0007 | STICKY_TIMEOUTS) /* Prototype for an lcall7 syscall handler. */ -typedef asmlinkage void (*lcall7_func)(struct pt_regs *); +typedef void (*lcall7_func)(struct pt_regs *); /* Description of an execution domain - personality range supported, diff -u --recursive --new-file v2.0.35/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v2.0.35/linux/include/linux/proc_fs.h Sun Nov 15 10:49:53 1998 +++ linux/include/linux/proc_fs.h Sun Nov 15 10:33:18 1998 @@ -141,6 +141,7 @@ PROC_SCSI_AM53C974, PROC_SCSI_SSC, PROC_SCSI_NCR53C406A, + PROC_SCSI_MEGARAID, PROC_SCSI_PPA, PROC_SCSI_ESP, PROC_SCSI_A3000, diff -u --recursive --new-file v2.0.35/linux/include/linux/rose.h linux/include/linux/rose.h --- v2.0.35/linux/include/linux/rose.h Sun Nov 15 10:49:53 1998 +++ linux/include/linux/rose.h Sun Nov 15 10:33:18 1998 @@ -7,9 +7,10 @@ #ifndef ROSE_KERNEL_H #define ROSE_KERNEL_H -#define PF_ROSE AF_ROSE #define ROSE_MTU 251 +#define ROSE_MAX_DIGIS 6 + #define ROSE_DEFER 1 #define ROSE_T1 2 #define ROSE_T2 3 @@ -21,8 +22,11 @@ #define SIOCRSGCAUSE (SIOCPROTOPRIVATE+0) #define SIOCRSSCAUSE (SIOCPROTOPRIVATE+1) #define SIOCRSL2CALL (SIOCPROTOPRIVATE+2) +#define SIOCRSSL2CALL (SIOCPROTOPRIVATE+2) #define SIOCRSACCEPT (SIOCPROTOPRIVATE+3) -#define SIOCRSCLRRT (SIOCPROTOPRIVATE+4) +#define SIOCRSCLRRT (SIOCPROTOPRIVATE+4) +#define SIOCRSGL2CALL (SIOCPROTOPRIVATE+5) +#define SIOCRSGFACILITIES (SIOCPROTOPRIVATE+6) #define ROSE_DTE_ORIGINATED 0x00 #define ROSE_NUMBER_BUSY 0x01 @@ -36,22 +40,30 @@ #define ROSE_SHIP_ABSENT 0x39 typedef struct { - char rose_addr[5]; + char rose_addr[5]; } rose_address; struct sockaddr_rose { unsigned short srose_family; rose_address srose_addr; ax25_address srose_call; - int srose_ndigis; + unsigned int srose_ndigis; ax25_address srose_digi; }; +struct full_sockaddr_rose { + unsigned short srose_family; + rose_address srose_addr; + ax25_address srose_call; + unsigned int srose_ndigis; + ax25_address srose_digis[ROSE_MAX_DIGIS]; +}; + struct rose_route_struct { rose_address address; unsigned short mask; ax25_address neighbour; - char device[16]; + char device[16]; unsigned char ndigis; ax25_address digipeaters[AX25_MAX_DIGIS]; }; @@ -60,5 +72,17 @@ unsigned char cause; unsigned char diagnostic; }; + +struct rose_facilities_struct { + rose_address source_addr, dest_addr; + ax25_address source_call, dest_call; + unsigned char source_ndigis, dest_ndigis; + ax25_address source_digis[ROSE_MAX_DIGIS]; + ax25_address dest_digis[ROSE_MAX_DIGIS]; + unsigned int rand; + rose_address fail_addr; + ax25_address fail_call; +}; + #endif diff -u --recursive --new-file v2.0.35/linux/include/linux/serial.h linux/include/linux/serial.h --- v2.0.35/linux/include/linux/serial.h Mon Aug 4 15:01:09 1997 +++ linux/include/linux/serial.h Sun Nov 15 10:33:18 1998 @@ -44,7 +44,8 @@ #define PORT_16550A 4 #define PORT_CIRRUS 5 #define PORT_16650 6 -#define PORT_MAX 6 +#define PORT_STARTECH 7 +#define PORT_MAX 7 /* * Definitions for async_struct (and serial_struct) flags field diff -u --recursive --new-file v2.0.35/linux/include/linux/shm.h linux/include/linux/shm.h --- v2.0.35/linux/include/linux/shm.h Thu Aug 14 13:31:28 1997 +++ linux/include/linux/shm.h Sun Nov 15 10:33:18 1998 @@ -63,6 +63,7 @@ asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr); asmlinkage int sys_shmdt (char *shmaddr); asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf); +extern void shm_unuse(unsigned int type); #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.0.35/linux/include/linux/skbuff.h linux/include/linux/skbuff.h --- v2.0.35/linux/include/linux/skbuff.h Wed Oct 15 15:22:08 1997 +++ linux/include/linux/skbuff.h Sun Nov 15 10:33:18 1998 @@ -112,6 +112,17 @@ unsigned char *end; /* End pointer */ void (*destructor)(struct sk_buff *); /* Destruct function */ __u16 redirport; /* Redirect port */ + + /* + * Keep this at the end then we wont break stuff. + */ +#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) + __u32 shapelatency; /* Latency on frame */ + __u32 shapeclock; /* Time it should go out */ + __u32 shapelen; /* Frame length in clocks */ + __u32 shapestamp; /* Stamp for shaper */ + __u16 shapepend; /* Pending */ +#endif }; #ifdef CONFIG_SKB_LARGE diff -u --recursive --new-file v2.0.35/linux/include/linux/types.h linux/include/linux/types.h --- v2.0.35/linux/include/linux/types.h Tue Oct 8 09:43:30 1996 +++ linux/include/linux/types.h Sun Nov 15 10:33:19 1998 @@ -1,6 +1,18 @@ #ifndef _LINUX_TYPES_H #define _LINUX_TYPES_H +#ifdef __i386__ +#if defined(__KERNEL__) && !defined(STDC_HEADERS) +#if ((__GNUC_MINOR__ >= 8) || (__GNUC_MAJOR >=3)) +#warning "This code is tested with gcc 2.7.2.x only. Using egcs/gcc 2.8.x needs" +#warning "additional patches that have not been sufficiently tested to include by" +#warning "default." +#warning "See http://www.suse.de/~florian/kernel+egcs.html for more information" +#error "Remove this if you have applied the gcc 2.8/egcs patches and wish to use them" +#endif +#endif +#endif + #include #include diff -u --recursive --new-file v2.0.35/linux/include/linux/wireless.h linux/include/linux/wireless.h --- v2.0.35/linux/include/linux/wireless.h Tue Aug 12 15:01:27 1997 +++ linux/include/linux/wireless.h Sun Nov 15 10:33:19 1998 @@ -245,7 +245,7 @@ /* Name : used to verify the presence of wireless extensions. * Name of the protocol/provider... */ - struct /* network id (or domain) : used to to */ + struct /* network id (or domain) : used to */ { /* create logical channels on the air */ __u32 nwid; /* value */ __u8 on; /* active/unactive nwid */ diff -u --recursive --new-file v2.0.35/linux/include/net/rose.h linux/include/net/rose.h --- v2.0.35/linux/include/net/rose.h Sun Nov 15 10:49:53 1998 +++ linux/include/net/rose.h Sun Nov 15 10:33:19 1998 @@ -18,6 +18,7 @@ #define ROSE_Q_BIT 0x80 #define ROSE_D_BIT 0x40 #define ROSE_M_BIT 0x10 +#define M_BIT 0x10 #define ROSE_CALL_REQUEST 0x0B #define ROSE_CALL_ACCEPTED 0x0F @@ -55,25 +56,30 @@ #define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */ #define ROSE_DEFAULT_FAIL_TIMEOUT (120 * ROSE_SLOWHZ) /* Time until link considered usable */ #define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */ -#define ROSE_DEFAULT_WINDOW_SIZE 3 /* Default window value */ +#define ROSE_DEFAULT_WINDOW_SIZE 7 /* Default window value */ #define ROSE_MODULUS 8 #define ROSE_MAX_PACKET_SIZE 251 /* Maximum Packet Size */ +#define ROSE_MAX_WINDOW_LEN ((AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 300) * 7) + #define ROSE_COND_ACK_PENDING 0x01 #define ROSE_COND_PEER_RX_BUSY 0x02 #define ROSE_COND_OWN_RX_BUSY 0x04 -#define FAC_NATIONAL 0x00 -#define FAC_CCITT 0x0F +#define FAC_NATIONAL 0x00 +#define FAC_CCITT 0x0F -#define FAC_NATIONAL_RAND 0x7F -#define FAC_NATIONAL_FLAGS 0x3F +#define FAC_NATIONAL_RAND 0x7F +#define FAC_NATIONAL_FLAGS 0x3F #define FAC_NATIONAL_DEST_DIGI 0xE9 #define FAC_NATIONAL_SRC_DIGI 0xEB +#define FAC_NATIONAL_FAIL_CALL 0xED +#define FAC_NATIONAL_FAIL_ADD 0xEE +#define FAC_NATIONAL_DIGIS 0xEF -#define FAC_CCITT_DEST_NSAP 0xC9 -#define FAC_CCITT_SRC_NSAP 0xCB +#define FAC_CCITT_DEST_NSAP 0xC9 +#define FAC_CCITT_SRC_NSAP 0xCB struct rose_neigh { struct rose_neigh *next; @@ -91,28 +97,29 @@ struct timer_list timer; }; +#define ROSE_MAX_ALTERNATE 3 struct rose_node { struct rose_node *next; rose_address address; unsigned short mask; unsigned char count; - struct rose_neigh *neighbour[3]; + struct rose_neigh *neighbour[ROSE_MAX_ALTERNATE]; }; +typedef struct { + unsigned int lci; + struct rose_neigh *neigh; + unsigned short vs, vr, va, vl; + unsigned short pending; + unsigned char state, condition; + struct timer_list timer; +} rose_tr; + struct rose_route { struct rose_route *next; rose_address src_addr, dest_addr; ax25_address src_call, dest_call; - unsigned int lci1, lci2; - struct rose_neigh *neigh1, *neigh2; - unsigned int rand; -}; - -struct rose_facilities { - rose_address source_addr, dest_addr; - ax25_address source_call, dest_call; - unsigned char source_ndigis, dest_ndigis; - ax25_address source_digi, dest_digi; + rose_tr tr1, tr2; unsigned int rand; }; @@ -120,7 +127,8 @@ rose_address source_addr, dest_addr; ax25_address source_call, dest_call; unsigned char source_ndigis, dest_ndigis; - ax25_address source_digi, dest_digi; + ax25_address source_digis[ROSE_MAX_DIGIS]; + ax25_address dest_digis[ROSE_MAX_DIGIS]; struct rose_neigh *neighbour; struct device *device; unsigned int lci, rand; @@ -129,6 +137,12 @@ unsigned short vs, vr, va, vl; unsigned short timer; unsigned short t1, t2, t3, hb; +#ifdef M_BIT + unsigned short fraglen; + struct sk_buff_head frag_queue; +#endif + struct sk_buff_head ack_queue; + struct rose_facilities_struct facilities; struct sock *sk; /* Backlink to socket */ } rose_cb; @@ -170,14 +184,19 @@ extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char); extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *); +/* rose_loopback.c */ +extern void rose_loopback_init(void); +extern void rose_loopback_clear(void); +extern int rose_loopback_queue(struct sk_buff *, struct rose_neigh *); + /* rose_out.c */ extern void rose_kick(struct sock *); extern void rose_enquiry_response(struct sock *); -extern void rose_check_iframes_acked(struct sock *, unsigned short); /* rose_route.c */ extern void rose_rt_device_down(struct device *); extern void rose_link_device_down(struct device *); +extern void rose_clean_neighbour(struct rose_neigh *); extern struct device *rose_dev_first(void); extern struct device *rose_dev_get(rose_address *); extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *); @@ -193,10 +212,11 @@ /* rose_subr.c */ extern void rose_clear_queues(struct sock *); +extern void rose_frames_acked(struct sock *, unsigned short); extern int rose_validate_nr(struct sock *, unsigned short); extern void rose_write_internal(struct sock *, int); extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); -extern int rose_parse_facilities(struct sk_buff *, struct rose_facilities *); +extern int rose_parse_facilities(unsigned char *, struct rose_facilities_struct *); extern int rose_create_facilities(unsigned char *, rose_cb *); /* rose_timer.c */ @@ -205,5 +225,9 @@ /* sysctl_net_rose.c */ extern void rose_register_sysctl(void); extern void rose_unregister_sysctl(void); + +/* rose_transit.c */ +void rose_transit(struct sk_buff *, rose_tr *, rose_tr *); +void rose_init_transit(rose_tr *, unsigned int, struct rose_neigh *); #endif diff -u --recursive --new-file v2.0.35/linux/include/net/sock.h linux/include/net/sock.h --- v2.0.35/linux/include/net/sock.h Sun Nov 15 10:49:53 1998 +++ linux/include/net/sock.h Sun Nov 15 10:33:19 1998 @@ -350,7 +350,8 @@ * Moved solely for 2.0 to keep binary module compatibility stuff straight. */ - unsigned short max_ack_backlog; + unsigned short max_ack_backlog; + struct sock *listening; }; /* diff -u --recursive --new-file v2.0.35/linux/init/main.c linux/init/main.c --- v2.0.35/linux/init/main.c Sun Nov 15 10:49:54 1998 +++ linux/init/main.c Sun Nov 15 10:33:19 1998 @@ -36,6 +36,9 @@ #ifdef CONFIG_ROOT_NFS #include #endif +#ifdef CONFIG_MTRR +#include +#endif #include @@ -104,6 +107,7 @@ extern void ppa_setup(char *str, int *ints); extern void scsi_luns_setup(char *str, int *ints); extern void sound_setup(char *str, int *ints); +extern void apm_setup(char *str, int *ints); extern void reboot_setup(char *str, int *ints); #ifdef CONFIG_CDU31A extern void cdu31a_setup(char *str, int *ints); @@ -169,6 +173,9 @@ extern void wd33c93_setup (char *str, int *ints); extern void gvp11_setup (char *str, int *ints); +#ifdef CONFIG_CYCLADES +extern void cy_setup(char *str, int *ints); +#endif #ifdef CONFIG_DIGI extern void pcxx_setup(char *str, int *ints); #endif @@ -446,6 +453,9 @@ #if defined(CONFIG_GVP11_SCSI) { "gvp11=", gvp11_setup }, #endif +#ifdef CONFIG_CYCLADES + { "cyclades=", cy_setup }, +#endif #ifdef CONFIG_DIGI { "digi=", pcxx_setup }, #endif @@ -458,6 +468,9 @@ #ifdef CONFIG_BAYCOM { "baycom=", baycom_setup }, #endif +#ifdef CONFIG_APM + { "apm=", apm_setup }, +#endif { 0, 0 } }; @@ -939,6 +952,11 @@ arch_syms_export(); sti(); check_bugs(); + +#if defined(CONFIG_MTRR) && defined(__SMP__) + init_mtrr_config(); +#endif + printk(linux_banner); #ifdef __SMP__ diff -u --recursive --new-file v2.0.35/linux/ipc/shm.c linux/ipc/shm.c --- v2.0.35/linux/ipc/shm.c Mon Jul 13 13:46:42 1998 +++ linux/ipc/shm.c Sun Nov 15 10:33:19 1998 @@ -3,6 +3,7 @@ * Copyright (C) 1992, 1993 Krishna Balasubramanian * Many improvements/fixes by Bruno Haible. * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994. + * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli. */ #include @@ -657,6 +658,7 @@ oom(current); return BAD_PAGE; } + repeat: pte_val(pte) = shp->shm_pages[idx]; if (pte_present(pte)) { free_page (page); /* doesn't sleep */ @@ -664,11 +666,8 @@ } if (!pte_none(pte)) { read_swap_page(pte_val(pte), (char *) page); - pte_val(pte) = shp->shm_pages[idx]; - if (pte_present(pte)) { - free_page (page); /* doesn't sleep */ - goto done; - } + if (pte_val(pte) != shp->shm_pages[idx]) + goto repeat; swap_free(pte_val(pte)); shm_swp--; } @@ -698,6 +697,7 @@ int shm_swap (int prio, int dma) { pte_t page; + struct page *page_map; struct shmid_ds *shp; struct vm_area_struct *shmd; unsigned long swap_nr; @@ -732,7 +732,10 @@ pte_val(page) = shp->shm_pages[idx]; if (!pte_present(page)) goto check_table; - if (dma && !PageDMA(&mem_map[MAP_NR(pte_page(page))])) + page_map = &mem_map[MAP_NR(pte_page(page))]; + if (PageLocked(page_map)) + goto check_table; + if (dma && !PageDMA(page_map)) goto check_table; swap_attempts++; @@ -803,4 +806,66 @@ shm_swp++; shm_rss--; return 1; +} + +/* + * Free the swap entry and set the new pte for the shm page. + */ +static void shm_unuse_page(struct shmid_ds *shp, unsigned long idx, + unsigned long type) +{ + pte_t pte = __pte(shp->shm_pages[idx]); + unsigned long page, entry = shp->shm_pages[idx]; + + if (pte_none(pte)) + return; + if (pte_present(pte)) + { + /* + * Security check. Should be not needed... + */ + unsigned long page_nr = MAP_NR(pte_page(pte)); + if (page_nr >= MAP_NR(high_memory)) + { + printk("shm page mapped in virtual memory\n"); + return; + } + if (!in_swap_cache(page_nr)) + return; + if (SWP_TYPE(in_swap_cache(page_nr)) != type) + return; + printk("shm page in swap cache, trying to remove it!\n"); + delete_from_swap_cache(page_nr); + + shp->shm_pages[idx] = pte_val(pte_mkdirty(pte)); + return; + } + + if (SWP_TYPE(pte_val(pte)) != type) + return; + + /* + * Here we must swapin the pte and free the swap. + */ + page = get_free_page(GFP_KERNEL); + read_swap_page(pte_val(pte), (char *) page); + pte = pte_mkdirty(mk_pte(page, PAGE_SHARED)); + shp->shm_pages[idx] = pte_val(pte); + shm_rss++; + + swap_free(entry); + shm_swp--; +} + +/* + * unuse_shm() search for an eventually swapped out shm page. + */ +void shm_unuse(unsigned int type) +{ + int i, n; + + for (i = 0; i < SHMMNI; i++) + if (shm_segs[i] != IPC_UNUSED && shm_segs[i] != IPC_NOID) + for (n = 0; n < shm_segs[i]->shm_npages; n++) + shm_unuse_page(shm_segs[i], n, type); } diff -u --recursive --new-file v2.0.35/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.0.35/linux/kernel/ksyms.c Sun Nov 15 10:49:55 1998 +++ linux/kernel/ksyms.c Sun Nov 15 10:33:20 1998 @@ -78,6 +78,9 @@ extern void hard_reset_now(void); +extern void select_free_wait(select_table * p); +extern int select_check(int flag, select_table * wait, struct file * file); + struct symbol_table symbol_table = { #include #ifdef MODVERSIONS @@ -160,6 +163,10 @@ X(getblk), X(bread), X(breada), + + X(select_check), + X(select_free_wait), + X(__brelse), X(__bforget), X(ll_rw_block), @@ -298,6 +305,7 @@ X(kill_proc), X(kill_pg), X(kill_sl), + X(force_sig), /* misc */ X(panic), @@ -310,6 +318,7 @@ X(sys_call_table), X(hard_reset_now), X(_ctype), + X(_ctmp), X(get_random_bytes), /* Signal interfaces */ @@ -368,6 +377,13 @@ X(get_write_access), X(put_write_access), +#ifdef CONFIG_PROC_FS + X(proc_dir_inode_operations), +#endif + + /* Modular sound */ + X(sys_open), + X(sys_read), /******************************************************** * Do not add anything below this line, * as the stacked modules depend on this! diff -u --recursive --new-file v2.0.35/linux/kernel/sched.c linux/kernel/sched.c --- v2.0.35/linux/kernel/sched.c Sun Nov 15 10:49:55 1998 +++ linux/kernel/sched.c Sun Nov 15 10:33:20 1998 @@ -50,7 +50,7 @@ long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */ volatile struct timeval xtime; /* The current time */ -int tickadj = 500/HZ; /* microsecs */ +int tickadj = 500/HZ ? 500/HZ : 1; /* microsecs */ DECLARE_TASK_QUEUE(tq_timer); DECLARE_TASK_QUEUE(tq_immediate); diff -u --recursive --new-file v2.0.35/linux/kernel/sys.c linux/kernel/sys.c --- v2.0.35/linux/kernel/sys.c Mon Jul 13 13:46:42 1998 +++ linux/kernel/sys.c Sun Nov 15 10:33:20 1998 @@ -197,10 +197,13 @@ else if (!flag) C_A_D = 0; else if (flag == 0xCDEF0123) { +#ifdef CONFIG_SCSI_GDTH + gdth_halt(); +#endif printk(KERN_EMERG "System halted\n"); sys_kill(-1, SIGKILL); #if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF) - apm_set_power_state(APM_STATE_OFF); + apm_power_off(); #endif do_exit(0); } else diff -u --recursive --new-file v2.0.35/linux/mm/mmap.c linux/mm/mmap.c --- v2.0.35/linux/mm/mmap.c Mon Jul 13 13:46:42 1998 +++ linux/mm/mmap.c Sun Nov 15 10:33:20 1998 @@ -179,7 +179,7 @@ if (locks_verify_locked(file->f_inode)) return -EAGAIN; /* cevans -- whoops another append-only file flaw */ - if (IS_APPEND(file->f_inode) && (prot & PROT_WRITE)) + if (IS_APPEND(file->f_inode) && (file->f_mode & 2)) return -EACCES; /* fall through */ case MAP_PRIVATE: diff -u --recursive --new-file v2.0.35/linux/mm/swapfile.c linux/mm/swapfile.c --- v2.0.35/linux/mm/swapfile.c Mon Jul 13 13:46:42 1998 +++ linux/mm/swapfile.c Sun Nov 15 10:33:20 1998 @@ -5,6 +5,7 @@ * Swap reorganised 29.12.95, Stephen Tweedie */ +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include /* for blk_size */ +#include #include #include /* for cli()/sti() */ @@ -313,6 +315,9 @@ nr++; } free_page(page); +#ifdef CONFIG_SYSVIPC + shm_unuse(type); +#endif return 0; } diff -u --recursive --new-file v2.0.35/linux/mm/vmscan.c linux/mm/vmscan.c --- v2.0.35/linux/mm/vmscan.c Sun Nov 15 10:49:56 1998 +++ linux/mm/vmscan.c Sun Nov 15 10:33:20 1998 @@ -415,7 +415,7 @@ switch (state) { do { case 0: - if (shrink_mmap(i, dma, can_do_io)) + if (shrink_mmap(i, dma, 1)) return 1; state = 1; case 1: diff -u --recursive --new-file v2.0.35/linux/net/bridge/br.c linux/net/bridge/br.c --- v2.0.35/linux/net/bridge/br.c Tue Aug 26 11:05:34 1997 +++ linux/net/bridge/br.c Sun Nov 15 10:33:20 1998 @@ -921,10 +921,7 @@ skb->pkt_bridged = IS_BRIDGED; skb->arp = 1; /* do not resolve... */ skb->h.raw = skb->data + ETH_HLEN; - save_flags(flags); - cli(); - skb_queue_tail(dev->buffs, skb); - restore_flags(flags); + dev_queue_xmit(skb, dev, SOPRI_INTERACTIVE); return(0); } @@ -977,10 +974,8 @@ skb->pkt_bridged = IS_BRIDGED; skb->arp = 1; /* do not resolve... */ skb->h.raw = skb->data + ETH_HLEN; - save_flags(flags); - cli(); - skb_queue_tail(dev->buffs, skb); - restore_flags(flags); + + dev_queue_xmit(skb, dev, SOPRI_INTERACTIVE); return(0); } diff -u --recursive --new-file v2.0.35/linux/net/core/dev.c linux/net/core/dev.c --- v2.0.35/linux/net/core/dev.c Sun Nov 15 10:49:58 1998 +++ linux/net/core/dev.c Sun Nov 15 10:33:20 1998 @@ -1507,6 +1507,8 @@ extern int bpq_init(void); extern void sdla_setup(void); extern int dlci_setup(void); +extern int sm_init(void); +extern int baycom_init(void); int net_dev_init(void) { @@ -1545,6 +1547,12 @@ #endif #if defined(CONFIG_SDLA) sdla_setup(); +#endif +#if defined(CONFIG_BAYCOM) + baycom_init(); +#endif +#if defined(CONFIG_SOUNDMODEM) + sm_init(); #endif /* * SLHC if present needs attaching so other people see it diff -u --recursive --new-file v2.0.35/linux/net/core/firewall.c linux/net/core/firewall.c --- v2.0.35/linux/net/core/firewall.c Sun May 19 13:40:50 1996 +++ linux/net/core/firewall.c Sun Nov 15 10:33:21 1998 @@ -7,6 +7,7 @@ */ #include +#include #include #include diff -u --recursive --new-file v2.0.35/linux/net/core/skbuff.c linux/net/core/skbuff.c --- v2.0.35/linux/net/core/skbuff.c Tue Apr 8 08:47:47 1997 +++ linux/net/core/skbuff.c Sun Nov 15 10:33:21 1998 @@ -865,6 +865,11 @@ if(skb->lock==0) net_locked--; restore_flags(flags); + + if (skb->free == 3) { + skb->free = 1; + kfree_skb(skb, FREE_WRITE); + } } void dev_kfree_skb(struct sk_buff *skb, int mode) diff -u --recursive --new-file v2.0.35/linux/net/core/sock.c linux/net/core/sock.c --- v2.0.35/linux/net/core/sock.c Mon Jul 13 13:46:43 1998 +++ linux/net/core/sock.c Sun Nov 15 10:33:21 1998 @@ -241,6 +241,10 @@ /* Bind this socket to a particular device like "eth0", * as specified in an ifreq structure. If the device * is "", socket is NOT bound to a device. */ + + if(!suser()) + return -EPERM; + if (!valbool) { sk->bound_device = NULL; } else { diff -u --recursive --new-file v2.0.35/linux/net/ipv4/arp.c linux/net/ipv4/arp.c --- v2.0.35/linux/net/ipv4/arp.c Sun Nov 15 10:49:59 1998 +++ linux/net/ipv4/arp.c Sun Nov 15 10:33:21 1998 @@ -1771,6 +1771,12 @@ unsigned char *sha,*tha; u32 sip,tip; + if(skb->pkt_type == PACKET_OTHERHOST) + { + kfree_skb(skb, FREE_READ); + return 0; + } + /* * The hardware length of the packet should match the hardware length * of the device. Similarly, the hardware types should match. The diff -u --recursive --new-file v2.0.35/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c --- v2.0.35/linux/net/ipv4/ip_input.c Wed Sep 17 12:00:47 1997 +++ linux/net/ipv4/ip_input.c Sun Nov 15 10:33:21 1998 @@ -251,14 +251,6 @@ ip_statistics.IpInReceives++; - /* - * Account for the packet (even if the packet is - * not accepted by the firewall!). - */ - -#ifdef CONFIG_IP_ACCT - ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN); -#endif /* * Tag the ip header of this packet so we can find it @@ -294,6 +286,24 @@ */ skb_trim(skb,ntohs(iph->tot_len)); + + if(skb->len < (iph->ihl<<2)) + { + ip_statistics.IpInHdrErrors++; + kfree_skb(skb, FREE_WRITE); + return 0; + } + + /* + * Account for the packet (even if the packet is + * not accepted by the firewall!). We do this after + * the sanity checks and the additional ihl check + * so we dont account garbage as we might do before. + */ + +#ifdef CONFIG_IP_ACCT + ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN); +#endif /* * Try to select closest alias device, if any. diff -u --recursive --new-file v2.0.35/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c --- v2.0.35/linux/net/ipv4/ip_masq.c Mon Jul 13 13:46:43 1998 +++ linux/net/ipv4/ip_masq.c Sun Nov 15 10:33:21 1998 @@ -1515,32 +1515,35 @@ else { struct tcphdr *th; - skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1), + if(len>=sizeof(struct tcphdr)) + { + skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1), len - sizeof(struct tcphdr), 0); - tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,len,skb); + tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,len,skb); - /* Check if TCP FIN or RST */ - th = (struct tcphdr *)portptr; - if (th->fin) - { - ms->flags |= IP_MASQ_F_SAW_FIN_IN; - } - if (th->rst) - { - ms->flags |= IP_MASQ_F_SAW_RST; - } + /* Check if TCP FIN or RST */ + th = (struct tcphdr *)portptr; + if (th->fin) + { + ms->flags |= IP_MASQ_F_SAW_FIN_IN; + } + if (th->rst) + { + ms->flags |= IP_MASQ_F_SAW_RST; + } - /* Now set the timeouts */ - if (ms->flags & IP_MASQ_F_SAW_RST) - { - timeout = 1; - } - else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN) - { - timeout = ip_masq_expire->tcp_fin_timeout; + /* Now set the timeouts */ + if (ms->flags & IP_MASQ_F_SAW_RST) + { + timeout = 1; + } + else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN) + { + timeout = ip_masq_expire->tcp_fin_timeout; + } + else timeout = ip_masq_expire->tcp_timeout; } - else timeout = ip_masq_expire->tcp_timeout; - } + } ip_masq_set_expire(ms, timeout); ip_send_check(iph); #ifdef DEBUG_CONFIG_IP_MASQUERADE diff -u --recursive --new-file v2.0.35/linux/net/ipv4/ip_masq_quake.c linux/net/ipv4/ip_masq_quake.c --- v2.0.35/linux/net/ipv4/ip_masq_quake.c Tue Dec 2 13:52:33 1997 +++ linux/net/ipv4/ip_masq_quake.c Sun Nov 15 10:33:21 1998 @@ -20,7 +20,6 @@ * */ -#include #include #include diff -u --recursive --new-file v2.0.35/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c --- v2.0.35/linux/net/ipv4/tcp.c Mon Jul 13 13:46:43 1998 +++ linux/net/ipv4/tcp.c Sun Nov 15 10:33:21 1998 @@ -516,7 +516,7 @@ * load is kept track of, if it is zero there is a strong * likely hood that there is a zero length chain we will * find with a small amount of searching, else the load is - * what we shoot for for when the chains all have at least + * what we shoot for when the chains all have at least * one entry. The base helps us walk the chains in an * order such that a good chain is found as quickly as possible. -DaveM */ @@ -943,7 +943,7 @@ return 1; case SEL_EX: - if (sk->urg_data) + if (sk->urg_data & URG_VALID) return 1; break; } @@ -1976,8 +1976,15 @@ if(sk->state == TCP_LISTEN) { - /* Special case */ + /* + * Special case + */ tcp_set_state(sk, TCP_CLOSE); + /* + * Our children must die before we do now that + * sk->listening exists. It was right anyway but + * dont break this assumption. + */ tcp_close_pending(sk); release_sock(sk); sk->dead = 1; @@ -2046,11 +2053,12 @@ tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT); } - sk->dead = 1; release_sock(sk); if(sk->state == TCP_CLOSE) tcp_v4_unhash(sk); + + sk->dead = 1; } diff -u --recursive --new-file v2.0.35/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c --- v2.0.35/linux/net/ipv4/tcp_input.c Sun Nov 15 10:49:59 1998 +++ linux/net/ipv4/tcp_input.c Sun Nov 15 10:33:22 1998 @@ -44,6 +44,8 @@ * then use it for ToS is even more * broken. * + * George Baeslack : SIGIO delivery on accept() bug that + * affected sun jdk. */ #include @@ -544,7 +546,10 @@ /* If the socket is dead, don't accept the connection. */ if (!sk->dead) { - sk->data_ready(sk,0); + /* + * This must wait for 3 way completion. + * sk->data_ready(sk,0); + */ } else { @@ -772,6 +777,7 @@ newsk->acked_seq = skb->seq + 1; newsk->copied_seq = skb->seq + 1; newsk->socket = NULL; + newsk->listening = sk; /* * Grab the ttl and tos values and use them @@ -886,7 +892,7 @@ /* If the socket is dead, don't accept the connection. */ if (!sk->dead) { - sk->data_ready(sk,0); + /*sk->data_ready(sk,0); */ } else { @@ -1032,6 +1038,7 @@ newsk->acked_seq = skb->seq; newsk->copied_seq = skb->seq; newsk->socket = NULL; + newsk->listening = sk; /* * Grab the ttl and tos values and use them @@ -1663,6 +1670,19 @@ if (sk->state==TCP_SYN_RECV) { tcp_set_state(sk, TCP_ESTABLISHED); + + /* + * We have a listening socket owning us. Wake it for + * the accept. + */ + + if ( sk->listening ) + { + /* The listener may be sk->dead. Dont worry + data_ready traps this */ + sk->data_ready(sk->listening,0); + sk->listening = NULL; + } /* Must check for peer advertising zero sized window * or else we get a sk->{mtu,mss} of zero and thus bomb out @@ -2496,10 +2516,14 @@ * then it's a new connection */ - if (sk->state == TCP_SYN_RECV && th->syn && skb->seq+1 == sk->acked_seq) + if (sk->state == TCP_SYN_RECV) { - kfree_skb(skb, FREE_READ); - return 0; + if(th->syn && skb->seq+1 == sk->acked_seq) + { + kfree_skb(skb, FREE_READ); + return 0; + } + goto rfc_step4; } /* @@ -2588,6 +2612,7 @@ sk->rtt = 0; sk->rto = TCP_TIMEOUT_INIT; sk->mdev = TCP_TIMEOUT_INIT; + goto rfc_step6; } else { @@ -2614,10 +2639,11 @@ kfree_skb(skb, FREE_READ); return 0; } + /* - * SYN_RECV with data maybe.. drop through + * Data maybe.. drop through */ - goto rfc_step6; + } /* @@ -2676,6 +2702,8 @@ #endif } +rfc_step4: /* I'll clean this up later */ + /* * We are now in normal data flow (see the step list in the RFC) * Note most of these are inline now. I'll inline the lot when @@ -2717,8 +2745,13 @@ * Process the ACK */ + if(!th->ack) + { + kfree_skb(skb, FREE_WRITE); + return 0; + } - if(th->ack && !tcp_ack(sk,th,skb->ack_seq,len)) + if(!tcp_ack(sk,th,skb->ack_seq,len)) { /* * Our three way handshake failed. @@ -2732,8 +2765,7 @@ return 0; } -rfc_step6: /* I'll clean this up later */ - +rfc_step6: /* * If the accepted buffer put us over our queue size we * now drop it (we must process the ack first to avoid diff -u --recursive --new-file v2.0.35/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c --- v2.0.35/linux/net/ipv4/tcp_output.c Sun Nov 15 10:49:59 1998 +++ linux/net/ipv4/tcp_output.c Sun Nov 15 10:33:22 1998 @@ -858,7 +858,7 @@ struct tcphdr *th =(struct tcphdr *)&sk->dummy_th; struct tcphdr *t1; struct sk_buff *buff; - struct device *dev=NULL; + struct device *dev=sk->bound_device; int tmp; buff = sock_wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL); @@ -962,7 +962,7 @@ struct tcphdr *t1; unsigned char *ptr; struct sk_buff * buff; - struct device *ndev=NULL; + struct device *ndev=newsk->bound_device; int tmp; buff = sock_wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC); @@ -1121,7 +1121,7 @@ { struct sk_buff *buff; struct tcphdr *t1; - struct device *dev = NULL; + struct device *dev = sk->bound_device; int tmp; if(sk->zapped) @@ -1216,7 +1216,7 @@ { struct sk_buff *buff,*skb; struct tcphdr *t1; - struct device *dev=NULL; + struct device *dev=sk->bound_device; int tmp; if (sk->zapped) diff -u --recursive --new-file v2.0.35/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c --- v2.0.35/linux/net/netrom/af_netrom.c Sun Nov 15 10:49:59 1998 +++ linux/net/netrom/af_netrom.c Sun Nov 15 10:33:22 1998 @@ -237,7 +237,7 @@ /* * Find a connected NET/ROM socket given their circuit IDs. */ -static struct sock *nr_find_peer(unsigned char index, unsigned char id) +static struct sock *nr_find_peer(unsigned char index, unsigned char id, ax25_address *dest) { struct sock *s; unsigned long flags; @@ -246,7 +246,7 @@ cli(); for (s = nr_list; s != NULL; s = s->next) { - if (s->protinfo.nr->your_index == index && s->protinfo.nr->your_id == id) { + if (s->protinfo.nr->your_index == index && s->protinfo.nr->your_id == id && ax25cmp(&s->protinfo.nr->dest_addr, dest) == 0) { restore_flags(flags); return s; } @@ -936,10 +936,10 @@ if (circuit_index == 0 && circuit_id == 0) { if (frametype == NR_CONNACK && flags == NR_CHOKE_FLAG) - sk = nr_find_peer(peer_circuit_index, peer_circuit_id); + sk = nr_find_peer(peer_circuit_index, peer_circuit_id, src); } else { if (frametype == NR_CONNREQ) - sk = nr_find_peer(circuit_index, circuit_id); + sk = nr_find_peer(circuit_index, circuit_id, src); else sk = nr_find_socket(circuit_index, circuit_id); } diff -u --recursive --new-file v2.0.35/linux/net/netrom/nr_loopback.c linux/net/netrom/nr_loopback.c --- v2.0.35/linux/net/netrom/nr_loopback.c Sun Nov 15 10:49:59 1998 +++ linux/net/netrom/nr_loopback.c Sun Nov 15 10:33:22 1998 @@ -11,6 +11,8 @@ * * History * NET/ROM 006 Tomi(OH2BNS) Created this file. + * Changed the way the loopback + * queue is consumed. * */ @@ -53,15 +55,16 @@ { struct sk_buff *skbn; - skbn = skb_copy(skb, GFP_ATOMIC); + skbn = skb_clone(skb, GFP_ATOMIC); kfree_skb(skb, FREE_WRITE); - if (skbn != NULL) + if (skbn != NULL) { skb_queue_tail(&loopback_queue, skbn); - if (!nr_loopback_running()) - nr_set_loopback_timer(); + if (!nr_loopback_running()) + nr_set_loopback_timer(); + } return 1; } @@ -85,16 +88,16 @@ ax25_address *nr_dest; struct device *dev; - while ((skb = skb_dequeue(&loopback_queue)) != NULL) { + if ((skb = skb_dequeue(&loopback_queue)) != NULL) { nr_dest = (ax25_address *)(skb->data + 7); - if ((dev = nr_dev_get(nr_dest)) == NULL) { - kfree_skb(skb, FREE_READ); - continue; - } + dev = nr_dev_get(nr_dest); - if (nr_rx_frame(skb, dev) == 0) + if (dev == NULL || nr_rx_frame(skb, dev) == 0) kfree_skb(skb, FREE_READ); + + if (!skb_queue_empty(&loopback_queue) && !nr_loopback_running()) + nr_set_loopback_timer(); } } diff -u --recursive --new-file v2.0.35/linux/net/rose/Makefile linux/net/rose/Makefile --- v2.0.35/linux/net/rose/Makefile Sun Nov 15 10:50:02 1998 +++ linux/net/rose/Makefile Sun Nov 15 10:33:22 1998 @@ -8,7 +8,8 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := rose.o -O_OBJS := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o +O_OBJS := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o \ +rose_loopback.o rose_out.o rose_route.o rose_subr.o rose_timer.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.0.35/linux/net/rose/af_rose.c linux/net/rose/af_rose.c --- v2.0.35/linux/net/rose/af_rose.c Sun Nov 15 10:50:02 1998 +++ linux/net/rose/af_rose.c Sun Nov 15 10:33:22 1998 @@ -1,5 +1,5 @@ /* - * ROSE release 003 + * ROSE release 006 * * This code REQUIRES 2.1.0 or higher/ NET3.029 * @@ -14,8 +14,13 @@ * Terry (VK2KTJ) Added support for variable length * address masks. * ROSE 002 Jonathan(G4KLX) Changed hdrincl to qbitincl. - * Added random number facilities entry. + * Added random number facilities entry. * ROSE 003 Jonathan(G4KLX) Added use count to neighbour. + * ROSE 004 Jean-Paul(F6FBB) Added LoopBack, M-Bit and + * FPAC facilities. + * ROSE 005 Jean-Paul(F6FBB) Added rose_clean_neighbour + * ROSE 006 Jean-Paul(F6FBB) Accept up to 6 digis + * Fixed a possible loop in facilities */ #include @@ -198,6 +203,7 @@ for (s = rose_list; s != NULL; s = s->next) { if (s->protinfo.rose->neighbour == neigh) { + rose_clear_queues(s); /* F6FBB */ s->protinfo.rose->cause = ROSE_OUT_OF_ORDER; s->protinfo.rose->diagnostic = 0; s->protinfo.rose->state = ROSE_STATE_0; @@ -221,6 +227,7 @@ for (s = rose_list; s != NULL; s = s->next) { if (s->protinfo.rose->device == dev) { + rose_clear_queues(s); /* F6FBB */ s->protinfo.rose->cause = ROSE_OUT_OF_ORDER; s->protinfo.rose->diagnostic = 0; s->protinfo.rose->state = ROSE_STATE_0; @@ -542,7 +549,7 @@ sk->protinfo.rose->dest_ndigis = 0; memset(&sk->protinfo.rose->dest_addr, '\0', ROSE_ADDR_LEN); memset(&sk->protinfo.rose->dest_call, '\0', AX25_ADDR_LEN); - memset(&sk->protinfo.rose->dest_digi, '\0', AX25_ADDR_LEN); + memset(sk->protinfo.rose->dest_digis, '\0', AX25_ADDR_LEN*ROSE_MAX_DIGIS); sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; return 0; @@ -579,7 +586,11 @@ skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); skb_queue_head_init(&sk->back_log); - + skb_queue_head_init(&rose->ack_queue); +#ifdef M_BIT + skb_queue_head_init(&rose->frag_queue); + rose->fraglen = 0; +#endif init_timer(&sk->timer); sk->socket = sock; @@ -629,6 +640,11 @@ skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); skb_queue_head_init(&sk->back_log); + skb_queue_head_init(&rose->ack_queue); +#ifdef M_BIT + skb_queue_head_init(&rose->frag_queue); + rose->fraglen = 0; +#endif init_timer(&sk->timer); @@ -728,16 +744,23 @@ struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; struct device *dev; ax25_address *user, *source; + int n; if (sk->zapped == 0) return -EINVAL; - if (addr_len != sizeof(struct sockaddr_rose)) + if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose)) return -EINVAL; if (addr->srose_family != AF_ROSE) return -EINVAL; + if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1) + return -EINVAL; + + if (addr->srose_ndigis > ROSE_MAX_DIGIS) + return -EINVAL; + if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) { if (sk->debug) printk("ROSE: bind failed: invalid address\n"); @@ -752,13 +775,19 @@ user = source; } - sk->protinfo.rose->source_addr = addr->srose_addr; - sk->protinfo.rose->source_call = *user; - sk->protinfo.rose->device = dev; - - if (addr->srose_ndigis == 1) { - sk->protinfo.rose->source_ndigis = 1; - sk->protinfo.rose->source_digi = addr->srose_digi; + sk->protinfo.rose->source_addr = addr->srose_addr; + sk->protinfo.rose->source_call = *user; + sk->protinfo.rose->device = dev; + sk->protinfo.rose->source_ndigis = addr->srose_ndigis; + + if (addr_len == sizeof(struct full_sockaddr_rose)) { + struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; + for (n = 0 ; n < addr->srose_ndigis ; n++) + sk->protinfo.rose->source_digis[n] = full_addr->srose_digis[n]; + } else { + if (sk->protinfo.rose->source_ndigis == 1) { + sk->protinfo.rose->source_digis[0] = addr->srose_digi; + } } rose_insert_socket(sk); @@ -778,6 +807,7 @@ unsigned char cause, diagnostic; ax25_address *user; struct device *dev; + int n; if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { sock->state = SS_CONNECTED; @@ -795,14 +825,27 @@ sk->state = TCP_CLOSE; sock->state = SS_UNCONNECTED; - if (addr_len != sizeof(struct sockaddr_rose)) + if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose)) return -EINVAL; if (addr->srose_family != AF_ROSE) return -EINVAL; - if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL) + if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1) + return -EINVAL; + + if (addr->srose_ndigis > ROSE_MAX_DIGIS) + return -EINVAL; + + /* Source + Destination digis should not exceed ROSE_MAX_DIGIS */ + if ((sk->protinfo.rose->source_ndigis + addr->srose_ndigis) > ROSE_MAX_DIGIS) + return -EINVAL; + + if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL) { + sk->protinfo.rose->cause = cause; + sk->protinfo.rose->diagnostic = diagnostic; return -ENETUNREACH; + } if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0) return -ENETUNREACH; @@ -823,13 +866,19 @@ rose_insert_socket(sk); /* Finish the bind */ } - sk->protinfo.rose->dest_addr = addr->srose_addr; - sk->protinfo.rose->dest_call = addr->srose_call; - sk->protinfo.rose->rand = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci; - - if (addr->srose_ndigis == 1) { - sk->protinfo.rose->dest_ndigis = 1; - sk->protinfo.rose->dest_digi = addr->srose_digi; + sk->protinfo.rose->dest_addr = addr->srose_addr; + sk->protinfo.rose->dest_call = addr->srose_call; + sk->protinfo.rose->rand = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci; + sk->protinfo.rose->dest_ndigis = addr->srose_ndigis; + + if (addr_len == sizeof(struct full_sockaddr_rose)) { + struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr; + for (n = 0 ; n < addr->srose_ndigis ; n++) + sk->protinfo.rose->dest_digis[n] = full_addr->srose_digis[n]; + } else { + if (sk->protinfo.rose->dest_ndigis == 1) { + sk->protinfo.rose->dest_digis[0] = addr->srose_digi; + } } /* Move to connecting socket, start sending Connect Requests */ @@ -937,29 +986,44 @@ { struct sockaddr_rose *srose = (struct sockaddr_rose *)uaddr; struct sock *sk = (struct sock *)sock->data; + int n; if (peer != 0) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; srose->srose_family = AF_ROSE; - srose->srose_ndigis = 0; srose->srose_addr = sk->protinfo.rose->dest_addr; srose->srose_call = sk->protinfo.rose->dest_call; - if (sk->protinfo.rose->dest_ndigis == 1) { - srose->srose_ndigis = 1; - srose->srose_digi = sk->protinfo.rose->dest_digi; + srose->srose_ndigis = sk->protinfo.rose->dest_ndigis; + if (*uaddr_len >= sizeof(struct full_sockaddr_rose)) { + struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)uaddr; + for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) + full_srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n]; + *uaddr_len = sizeof(struct full_sockaddr_rose); + } else { + if (sk->protinfo.rose->dest_ndigis >= 1) { + srose->srose_ndigis = 1; + srose->srose_digi = sk->protinfo.rose->dest_digis[0]; + } + *uaddr_len = sizeof(struct sockaddr_rose); } - *uaddr_len = sizeof(struct sockaddr_rose); } else { srose->srose_family = AF_ROSE; - srose->srose_ndigis = 0; srose->srose_addr = sk->protinfo.rose->source_addr; srose->srose_call = sk->protinfo.rose->source_call; - if (sk->protinfo.rose->source_ndigis == 1) { - srose->srose_ndigis = 1; - srose->srose_digi = sk->protinfo.rose->source_digi; + srose->srose_ndigis = sk->protinfo.rose->source_ndigis; + if (*uaddr_len >= sizeof(struct full_sockaddr_rose)) { + struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)uaddr; + for (n = 0 ; n < sk->protinfo.rose->source_ndigis ; n++) + full_srose->srose_digis[n] = sk->protinfo.rose->source_digis[n]; + *uaddr_len = sizeof(struct full_sockaddr_rose); + } else { + if (sk->protinfo.rose->source_ndigis >= 1) { + srose->srose_ndigis = 1; + srose->srose_digi = sk->protinfo.rose->source_digis[sk->protinfo.rose->source_ndigis-1]; + } + *uaddr_len = sizeof(struct sockaddr_rose); } - *uaddr_len = sizeof(struct sockaddr_rose); } return 0; @@ -969,14 +1033,21 @@ { struct sock *sk; struct sock *make; - struct rose_facilities facilities; - + struct rose_facilities_struct facilities; + int n, len; + skb->sk = NULL; /* Initially we don't know who it's for */ /* * skb->data points to the rose frame start */ - if (!rose_parse_facilities(skb, &facilities)) { + + len = (((skb->data[3] >> 4) & 0x0F) + 1) / 2; + len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2; + + memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); + + if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76); return 0; } @@ -998,13 +1069,16 @@ make->protinfo.rose->dest_addr = facilities.dest_addr; make->protinfo.rose->dest_call = facilities.dest_call; make->protinfo.rose->dest_ndigis = facilities.dest_ndigis; - make->protinfo.rose->dest_digi = facilities.dest_digi; + for (n = 0 ; n < facilities.dest_ndigis ; n++) + make->protinfo.rose->dest_digis[n] = facilities.dest_digis[n]; make->protinfo.rose->source_addr = facilities.source_addr; make->protinfo.rose->source_call = facilities.source_call; make->protinfo.rose->source_ndigis = facilities.source_ndigis; - make->protinfo.rose->source_digi = facilities.source_digi; + for (n = 0 ; n < facilities.source_ndigis ; n++) + make->protinfo.rose->source_digis[n]= facilities.source_digis[n]; make->protinfo.rose->neighbour = neigh; make->protinfo.rose->device = dev; + make->protinfo.rose->facilities = facilities; make->protinfo.rose->neighbour->use++; @@ -1040,10 +1114,10 @@ struct sock *sk = (struct sock *)sock->data; struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name; int err; - struct sockaddr_rose srose; + struct full_sockaddr_rose srose; struct sk_buff *skb; unsigned char *asmptr; - int size, qbit = 0; + int n, size, qbit = 0; if (sk->err) return sock_error(sk); @@ -1063,15 +1137,19 @@ return -ENETUNREACH; if (usrose != NULL) { - if (msg->msg_namelen < sizeof(srose)) + if (msg->msg_namelen != sizeof(struct sockaddr_rose) && msg->msg_namelen != sizeof(struct full_sockaddr_rose)) return -EINVAL; - srose = *usrose; + memset(&srose, 0, sizeof(struct full_sockaddr_rose)); + memcpy(&srose, usrose, msg->msg_namelen); if (rosecmp(&sk->protinfo.rose->dest_addr, &srose.srose_addr) != 0 || ax25cmp(&sk->protinfo.rose->dest_call, &srose.srose_call) != 0) return -EISCONN; - if (srose.srose_ndigis == 1 && sk->protinfo.rose->dest_ndigis == 1) { - if (ax25cmp(&sk->protinfo.rose->dest_digi, &srose.srose_digi) != 0) - return -EISCONN; + if (srose.srose_ndigis != sk->protinfo.rose->dest_ndigis) + return -EISCONN; + if (srose.srose_ndigis == sk->protinfo.rose->dest_ndigis) { + for (n = 0 ; n < srose.srose_ndigis ; n++) + if (ax25cmp(&sk->protinfo.rose->dest_digis[n], &srose.srose_digis[n]) != 0) + return -EISCONN; } if (srose.srose_family != AF_ROSE) return -EINVAL; @@ -1082,12 +1160,9 @@ srose.srose_family = AF_ROSE; srose.srose_addr = sk->protinfo.rose->dest_addr; srose.srose_call = sk->protinfo.rose->dest_call; - srose.srose_ndigis = 0; - - if (sk->protinfo.rose->dest_ndigis == 1) { - srose.srose_ndigis = 1; - srose.srose_digi = sk->protinfo.rose->dest_digi; - } + srose.srose_ndigis = sk->protinfo.rose->dest_ndigis; + for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) + srose.srose_digis[n] = sk->protinfo.rose->dest_digis[n]; } if (sk->debug) @@ -1154,10 +1229,55 @@ return -ENOTCONN; } - skb_queue_tail(&sk->write_queue, skb); /* Shove it onto the queue */ +#ifdef M_BIT +#define ROSE_PACLEN (256-ROSE_MIN_LEN) + if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) { + unsigned char header[ROSE_MIN_LEN]; + struct sk_buff *skbn; + int frontlen; + int lg; + + /* Save a copy of the Header */ + memcpy(header, skb->data, ROSE_MIN_LEN); + skb_pull(skb, ROSE_MIN_LEN); - if (sk->protinfo.rose->state == ROSE_STATE_3) - rose_kick(sk); + frontlen = skb_headroom(skb); + + while (skb->len > 0) { + if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, 0, &err)) == NULL) + return err; + + skbn->sk = sk; + skbn->free = 1; + skbn->arp = 1; + + skb_reserve(skbn, frontlen); + + lg = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN; + + /* Copy the user data */ + memcpy(skb_put(skbn, lg), skb->data, lg); + skb_pull(skb, lg); + + /* Duplicate the Header */ + skb_push(skbn, ROSE_MIN_LEN); + memcpy(skbn->data, header, ROSE_MIN_LEN); + + if (skb->len > 0) + skbn->data[2] |= M_BIT; + + skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */ + } + + skb->free = 1; + kfree_skb(skb, FREE_WRITE); + } else { + skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */ + } +#else + skb_queue_tail(&sk->write_queue, skb); /* Shove it onto the queue */ +#endif + rose_kick(sk); return len; } @@ -1171,7 +1291,7 @@ int copied, qbit; unsigned char *asmptr; struct sk_buff *skb; - int er; + int n, er; if (sk->err) return sock_error(sk); @@ -1208,14 +1328,19 @@ srose->srose_family = AF_ROSE; srose->srose_addr = sk->protinfo.rose->dest_addr; srose->srose_call = sk->protinfo.rose->dest_call; - srose->srose_ndigis = 0; - - if (sk->protinfo.rose->dest_ndigis == 1) { - srose->srose_ndigis = 1; - srose->srose_digi = sk->protinfo.rose->dest_digi; + srose->srose_ndigis = sk->protinfo.rose->dest_ndigis; + if (*addr_len >= sizeof(struct full_sockaddr_rose)) { + struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)msg->msg_name; + for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++) + full_srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n]; + *addr_len = sizeof(struct full_sockaddr_rose); + } else { + if (sk->protinfo.rose->dest_ndigis >= 1) { + srose->srose_ndigis = 1; + srose->srose_digi = sk->protinfo.rose->dest_digis[0]; + } + *addr_len = sizeof(struct sockaddr_rose); } - - *addr_len = sizeof(*srose); } skb_free_datagram(sk, skb); @@ -1313,7 +1438,7 @@ return 0; } - case SIOCRSL2CALL: + case SIOCRSSL2CALL: if (!suser()) return -EPERM; if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0) return err; @@ -1324,6 +1449,12 @@ ax25_listen_register(&rose_callsign, NULL); return 0; + case SIOCRSGL2CALL: + if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(ax25_address))) != 0) + return err; + memcpy_tofs((void *)arg, &rose_callsign, sizeof(ax25_address)); + return 0; + case SIOCRSACCEPT: if (sk->protinfo.rose->state == ROSE_STATE_5) { rose_write_internal(sk, ROSE_CALL_ACCEPTED); @@ -1336,6 +1467,13 @@ } return 0; + case SIOCRSGFACILITIES: { + if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct rose_facilities_struct))) != 0) + return err; + memcpy_tofs((void *)arg, &sk->protinfo.rose->facilities, sizeof(struct rose_facilities_struct)); + return 0; + } + default: return dev_ioctl(cmd, (void *)arg); } @@ -1355,7 +1493,7 @@ cli(); - len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q Inode\n"); + len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci neigh st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q Inode\n"); for (s = rose_list; s != NULL; s = s->next) { if ((dev = s->protinfo.rose->device) == NULL) @@ -1372,9 +1510,10 @@ else callsign = ax2asc(&s->protinfo.rose->source_call); - len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %3d/%03d %5d %5d %ld\n", + len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %05d %d %d %d %d %3d %3d %3d %3d %3d %3d/%03d %5d %5d %ld\n", rose2asc(&s->protinfo.rose->source_addr), callsign, devname, s->protinfo.rose->lci & 0x0FFF, + (s->protinfo.rose->neighbour) ? s->protinfo.rose->neighbour->number : 0, s->protinfo.rose->state, s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va, s->protinfo.rose->timer / ROSE_SLOWHZ, @@ -1384,8 +1523,7 @@ s->protinfo.rose->hb / ROSE_SLOWHZ, 0, 0, s->wmem_alloc, s->rmem_alloc, - s->socket && SOCK_INODE(s->socket) ? - SOCK_INODE(s->socket)->i_ino : 0); + s->socket && SOCK_INODE(s->socket) ? SOCK_INODE(s->socket)->i_ino : 0); pos = begin + len; @@ -1468,7 +1606,11 @@ {"rose2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, {"rose3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, {"rose4", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, - {"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init} + {"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose6", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose8", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose9", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init} }; void rose_proto_init(struct net_proto *pro) @@ -1479,18 +1621,20 @@ sock_register(rose_proto_ops.family, &rose_proto_ops); register_netdevice_notifier(&rose_dev_notifier); - printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.3 for AX25.035 Linux 2.0\n"); + printk(KERN_INFO "F6FBB/G4KLX ROSE for Linux. Version 0.63 for AX25.035 Linux 2.0\n"); if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame)) printk(KERN_ERR "ROSE: unable to register protocol with AX.25\n"); if (!ax25_linkfail_register(rose_link_failed)) printk(KERN_ERR "ROSE: unable to register linkfail handler with AX.25\n"); - for (i = 0; i < 6; i++) + for (i = 0; i < 10; i++) register_netdev(&dev_rose[i]); rose_register_sysctl(); + rose_loopback_init(); + #ifdef CONFIG_PROC_FS proc_net_register(&proc_net_rose); proc_net_register(&proc_net_rose_neigh); @@ -1520,6 +1664,8 @@ proc_net_unregister(PROC_NET_RS_NODES); proc_net_unregister(PROC_NET_RS_ROUTES); #endif + rose_loopback_clear(); + rose_rt_free(); ax25_protocol_release(AX25_P_ROSE); @@ -1534,7 +1680,7 @@ sock_unregister(AF_ROSE); - for (i = 0; i < 6; i++) { + for (i = 0; i < 10; i++) { if (dev_rose[i].priv != NULL) { kfree(dev_rose[i].priv); dev_rose[i].priv = NULL; diff -u --recursive --new-file v2.0.35/linux/net/rose/changes.doc linux/net/rose/changes.doc --- v2.0.35/linux/net/rose/changes.doc Wed Dec 31 16:00:00 1969 +++ linux/net/rose/changes.doc Sun Nov 15 10:33:22 1998 @@ -0,0 +1,10 @@ +Changes information in rose driver by F6FBB + +980728 : Added link add and route add from remote application + An adjacent is not removed if no node. It points to the + dummy node (0000000000) + +980728 : Routing algorithm was falling to the next route if no choice + available for the selected address. Corrected. + +980728 : Connect did not report cause and diagnostic. Added. diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_in.c linux/net/rose/rose_in.c --- v2.0.35/linux/net/rose/rose_in.c Sun Nov 15 10:50:02 1998 +++ linux/net/rose/rose_in.c Sun Nov 15 10:33:22 1998 @@ -17,7 +17,6 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_in.c - * ROSE 003 Jonathan(G4KLX) Removed M bit processing. */ #include @@ -45,6 +44,45 @@ #include #include +static int rose_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) +{ + struct sk_buff *skbo, *skbn = skb; + + if (more) { + sk->protinfo.rose->fraglen += skb->len; + skb_queue_tail(&sk->protinfo.rose->frag_queue, skb); + return 0; + } + + if (!more && sk->protinfo.rose->fraglen > 0) { /* End of fragment */ + sk->protinfo.rose->fraglen += skb->len; + skb_queue_tail(&sk->protinfo.rose->frag_queue, skb); + + if ((skbn = alloc_skb(sk->protinfo.rose->fraglen, GFP_ATOMIC)) == NULL) + return 1; + + skbn->free = 1; + skbn->arp = 1; + skbn->sk = sk; + skbn->h.raw = skbn->data; + + skbo = skb_dequeue(&sk->protinfo.rose->frag_queue); + memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + kfree_skb(skbo, FREE_READ); + + while ((skbo = skb_dequeue(&sk->protinfo.rose->frag_queue)) != NULL) { + skb_pull(skbo, ROSE_MIN_LEN); + memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len); + kfree_skb(skbo, FREE_READ); + } + + sk->protinfo.rose->fraglen = 0; + } + + /* printk("FBB : lg=%ld\n", skbn->len); */ + return sock_queue_rcv_skb(sk, skbn); +} + /* * State machine for state 1, Awaiting Call Accepted State. * The handling of the timer(s) is in file rose_timer.c. @@ -52,6 +90,8 @@ */ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + int len; + switch (frametype) { case ROSE_CALL_ACCEPTED: @@ -80,6 +120,18 @@ if (!sk->dead) sk->state_change(sk); sk->dead = 1; + len = 5; /* Minimum size of the frame data */ + if (skb->len > len) { + /* Address block */ + len += 1; + len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2; + len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2; + + if (skb->len > len) { + /* Facilities */ + rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities); + } + } break; default: @@ -96,12 +148,27 @@ */ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + int len; + switch (frametype) { case ROSE_CLEAR_REQUEST: rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); sk->protinfo.rose->cause = skb->data[3]; sk->protinfo.rose->diagnostic = skb->data[4]; + len = 5; + if (skb->len > len) { + /* Address block */ + len += 1; + len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2; + len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2; + + if (skb->len > len) { + /* Facilities */ + rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities); + } + } + /* fall in next case ... */ case ROSE_CLEAR_CONFIRMATION: rose_clear_queues(sk); sk->protinfo.rose->neighbour->use--; @@ -129,6 +196,7 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m) { int queued = 0; + int len; switch (frametype) { @@ -155,14 +223,22 @@ if (!sk->dead) sk->state_change(sk); sk->dead = 1; + len = 5; + if (skb->len > len) { + /* Address block */ + len += 1; + len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2; + len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2; + + if (skb->len > len) { + /* Facilities */ + rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities); + } + } break; case ROSE_RR: case ROSE_RNR: - if (frametype == ROSE_RNR) - sk->protinfo.rose->condition |= ROSE_COND_PEER_RX_BUSY; - else - sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; if (!rose_validate_nr(sk, nr)) { rose_clear_queues(sk); rose_write_internal(sk, ROSE_RESET_REQUEST); @@ -174,10 +250,12 @@ sk->protinfo.rose->state = ROSE_STATE_4; sk->protinfo.rose->timer = sk->protinfo.rose->t2; } else { - if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) { - sk->protinfo.rose->va = nr; - } else { - rose_check_iframes_acked(sk, nr); + rose_frames_acked(sk, nr); + /* F6FBB : only set the flag ! */ + if (frametype == ROSE_RNR) + sk->protinfo.rose->condition |= ROSE_COND_PEER_RX_BUSY; + else { + sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; } } break; @@ -196,18 +274,26 @@ sk->protinfo.rose->timer = sk->protinfo.rose->t2; break; } - if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) { - sk->protinfo.rose->va = nr; - } else { - rose_check_iframes_acked(sk, nr); - } - if (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY) - break; + rose_frames_acked(sk, nr); if (ns == sk->protinfo.rose->vr) { - if (sock_queue_rcv_skb(sk, skb) == 0) { + if (rose_queue_rx_frame(sk, skb, m) == 0) { sk->protinfo.rose->vr = (sk->protinfo.rose->vr + 1) % ROSE_MODULUS; queued = 1; } else { + /* should never happen ! */ + rose_clear_queues(sk); + rose_write_internal(sk, ROSE_RESET_REQUEST); + sk->protinfo.rose->condition = 0x00; + sk->protinfo.rose->vs = 0; + sk->protinfo.rose->vr = 0; + sk->protinfo.rose->va = 0; + sk->protinfo.rose->vl = 0; + sk->protinfo.rose->state = ROSE_STATE_4; + sk->protinfo.rose->timer = sk->protinfo.rose->t2; + break; + } + /* F6FBB : check if room enough for a full window */ + if (sk->rmem_alloc > (sk->rcvbuf - ROSE_MAX_WINDOW_LEN)) { sk->protinfo.rose->condition |= ROSE_COND_OWN_RX_BUSY; } } @@ -240,6 +326,8 @@ */ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + int len; + switch (frametype) { case ROSE_RESET_REQUEST: @@ -268,6 +356,18 @@ if (!sk->dead) sk->state_change(sk); sk->dead = 1; + len = 5; + if (skb->len > len) { + /* Address block */ + len += 1; + len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2; + len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2; + + if (skb->len > len) { + /* Facilities */ + rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities); + } + } break; default: @@ -284,6 +384,8 @@ */ static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + int len; + switch (frametype) { case ROSE_CLEAR_REQUEST: @@ -299,6 +401,18 @@ if (!sk->dead) sk->state_change(sk); sk->dead = 1; + len = 5; + if (skb->len > len) { + /* Address block */ + len += 1; + len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2; + len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2; + + if (skb->len > len) { + /* Facilities */ + rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities); + } + } break; } diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_link.c linux/net/rose/rose_link.c --- v2.0.35/linux/net/rose/rose_link.c Sun Nov 15 10:50:02 1998 +++ linux/net/rose/rose_link.c Sun Nov 15 10:33:22 1998 @@ -101,7 +101,7 @@ else rose_call = &rose_callsign; - neigh->ax25 = ax25_send_frame(skb, 0, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); return (neigh->ax25 != NULL); } @@ -134,6 +134,8 @@ switch (frametype) { case ROSE_RESTART_REQUEST: + /* Stop all existing routes on this link - F6FBB */ + rose_clean_neighbour(neigh); neigh->t0timer = 0; neigh->restarted = 1; neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED); @@ -261,10 +263,17 @@ { struct sk_buff *skb; unsigned char *dptr; + struct device *first; int len; len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3; - + + first = rose_dev_first(); + if (first) { + /* F6FBB - Adding facilities */ + len += 6 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN; + } + if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) return; @@ -272,7 +281,7 @@ skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN); - dptr = skb_put(skb, ROSE_MIN_LEN + 3); + dptr = skb_put(skb, skb_tailroom(skb)); *dptr++ = AX25_P_ROSE; *dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI; @@ -281,18 +290,47 @@ *dptr++ = cause; *dptr++ = diagnostic; + if (first) { + /* F6FBB - Adding facilities */ + *dptr++ = 0x00; /* Address length */ + *dptr++ = 4 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN; /* Facilities length */ + *dptr++ = 0; + *dptr++ = FAC_NATIONAL; + *dptr++ = FAC_NATIONAL_FAIL_CALL; + *dptr++ = AX25_ADDR_LEN; + memcpy(dptr, &rose_callsign, AX25_ADDR_LEN); + dptr += AX25_ADDR_LEN; + *dptr++ = FAC_NATIONAL_FAIL_ADD; + *dptr++ = ROSE_ADDR_LEN + 1; + *dptr++ = ROSE_ADDR_LEN * 2; + memcpy(dptr, first->dev_addr, ROSE_ADDR_LEN); + } + if (!rose_send_frame(skb, neigh)) kfree_skb(skb, FREE_WRITE); } void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh) { + rose_address *dest_addr; unsigned char *dptr; #ifdef CONFIG_FIREWALL if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT) return; #endif + + /* + * Check to see if its for us, if it is put it onto the loopback + * queue. + */ + dest_addr = (rose_address *)(skb->data + 4); + + if ((neigh->dce_mode == -1) || (rose_dev_get(dest_addr) != NULL)) { + neigh->dce_mode = -1; + rose_loopback_queue(skb, neigh); + return; + } if (!rose_link_up(neigh)) neigh->restarted = 0; diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_loopback.c linux/net/rose/rose_loopback.c --- v2.0.35/linux/net/rose/rose_loopback.c Wed Dec 31 16:00:00 1969 +++ linux/net/rose/rose_loopback.c Sun Nov 15 10:33:22 1998 @@ -0,0 +1,135 @@ +/* + * ROSE release 003 + * + * This code REQUIRES 2.1.15 or higher/ NET3.038 + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * History + * ROSE 003 Jonathan(G4KLX) Created this file from nr_loopback.c. + * + */ + +#include +#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE) +#include +#include +#include +#include +#include +#include + +static struct sk_buff_head loopback_queue; +static struct timer_list loopback_timer; + +static void rose_set_loopback_timer(void); + +void rose_loopback_init(void) +{ + skb_queue_head_init(&loopback_queue); + + init_timer(&loopback_timer); +} + +void rose_loopback_clear(void) +{ + struct sk_buff *skb; + + del_timer(&loopback_timer); + + while ((skb = skb_dequeue(&loopback_queue)) != NULL) { + skb->sk = NULL; + kfree_skb(skb, FREE_READ); + } +} + +static int rose_loopback_running(void) +{ + return (loopback_timer.prev != NULL || loopback_timer.next != NULL); +} + +int rose_loopback_queue(struct sk_buff *skb, struct rose_neigh *neigh) +{ + struct sk_buff *skbn; + + skbn = skb_clone(skb, GFP_ATOMIC); + + kfree_skb(skb, FREE_WRITE); + + if (skbn != NULL) { + skbn->sk = (struct sock *)neigh; + skb_queue_tail(&loopback_queue, skbn); + } + + if (!rose_loopback_running()) + { + rose_set_loopback_timer(); + } + return 1; +} + +static void rose_loopback_timer(unsigned long); + +static void rose_set_loopback_timer(void) +{ + del_timer(&loopback_timer); + + loopback_timer.data = 0; + loopback_timer.function = &rose_loopback_timer; + loopback_timer.expires = jiffies + 10; + + add_timer(&loopback_timer); +} + +static void rose_loopback_timer(unsigned long param) +{ + struct sk_buff *skb; + struct rose_neigh *rose_neigh; + struct sock *sk; + struct device *dev; + rose_address *dest_addr; + unsigned int lci; + unsigned short frametype; + + while ((skb = skb_dequeue(&loopback_queue)) != NULL) { + rose_neigh = (struct rose_neigh *)skb->sk; + lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); + dest_addr = (rose_address *)(skb->data + 4); + + /* F1OAT : Patch the LCI for proper loopback operation */ + /* Work only if the system open less than 2048 VC !!! */ + + lci = 4096 - lci; + skb->data[0] = (lci >> 8) & 0x0F; + skb->data[1] = lci & 0xFF; + + skb->sk = NULL; + + frametype = skb->data[2]; + + if (frametype == ROSE_CALL_REQUEST) { + if ((dev = rose_dev_get(dest_addr)) == NULL) { + kfree_skb(skb, FREE_READ); + continue; + } + if (!rose_rx_call_request(skb, dev, rose_neigh, lci)) + kfree_skb(skb, FREE_READ); + } + else { + if ((sk = rose_find_socket(lci, rose_neigh)) == NULL) { + kfree_skb(skb, FREE_READ); + continue; + } + skb->h.raw = skb->data; + + if (rose_process_rx_frame(sk, skb) == 0) + kfree_skb(skb, FREE_READ); + } + } +} + +#endif diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_out.c linux/net/rose/rose_out.c --- v2.0.35/linux/net/rose/rose_out.c Sun Nov 15 10:50:02 1998 +++ linux/net/rose/rose_out.c Sun Nov 15 10:33:22 1998 @@ -11,7 +11,6 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_out.c - * ROSE 003 Jonathan(G4KLX) Removed M bit processing. */ #include @@ -55,39 +54,56 @@ void rose_kick(struct sock *sk) { - struct sk_buff *skb; - unsigned short end; + struct sk_buff *skb, *skbn; + unsigned short start, end; - del_timer(&sk->timer); + if (sk->protinfo.rose->state != ROSE_STATE_3) + return; - end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS; + if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) + return; - if (!(sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) && - sk->protinfo.rose->vs != end && - skb_peek(&sk->write_queue) != NULL) { - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ + if (skb_peek(&sk->write_queue) == NULL) + return; - skb = skb_dequeue(&sk->write_queue); + start = (skb_peek(&sk->protinfo.rose->ack_queue) == NULL) ? sk->protinfo.rose->va : sk->protinfo.rose->vs; + end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS; - do { - /* - * Transmit the frame. - */ - rose_send_iframe(sk, skb); + if (start == end) + return; - sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS; + sk->protinfo.rose->vs = start; - } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + /* + * Transmit data until either we're out of data to send or + * the window is full. + */ + + skb = skb_dequeue(&sk->write_queue); + + do { + if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { + skb_queue_head(&sk->write_queue, skb); + break; + } - sk->protinfo.rose->vl = sk->protinfo.rose->vr; - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = 0; - } + /* + * Transmit the frame copy. + */ + rose_send_iframe(sk, skbn); - rose_set_timer(sk); + sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS; + + /* + * Requeue the original data frame. + */ + skb_queue_tail(&sk->protinfo.rose->ack_queue, skb); + + } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + + sk->protinfo.rose->vl = sk->protinfo.rose->vr; + sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + sk->protinfo.rose->timer = 0; } /* @@ -105,16 +121,6 @@ sk->protinfo.rose->vl = sk->protinfo.rose->vr; sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; sk->protinfo.rose->timer = 0; -} - -void rose_check_iframes_acked(struct sock *sk, unsigned short nr) -{ - if (sk->protinfo.rose->vs == nr) { - sk->protinfo.rose->va = nr; - } else { - if (sk->protinfo.rose->va != nr) - sk->protinfo.rose->va = nr; - } } #endif diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_route.c linux/net/rose/rose_route.c --- v2.0.35/linux/net/rose/rose_route.c Sun Nov 15 10:50:02 1998 +++ linux/net/rose/rose_route.c Sun Nov 15 10:33:22 1998 @@ -54,6 +54,17 @@ static struct rose_route *rose_route_list = NULL; static void rose_remove_neigh(struct rose_neigh *); +static int rose_del_node(struct rose_route_struct *, struct device *); + +static int rose_is_null(rose_address *address) +{ + int i; + + for (i = 0 ; i < sizeof(rose_address) ; i++) + if (address->rose_addr[i]) + return 0; + return 1; +} /* * Add a new route to a node, and in the process add the node and the @@ -63,6 +74,7 @@ { struct rose_node *rose_node, *rose_tmpn, *rose_tmpp; struct rose_neigh *rose_neigh; + struct rose_route_struct dummy_route; unsigned long flags; int i; @@ -166,16 +178,22 @@ rose_neigh->count++; - return 0; - } - - /* We have space, slot it in */ - if (rose_node->count < 3) { + } else if (rose_node->count < ROSE_MAX_ALTERNATE) { + /* We have space, slot it in */ rose_node->neighbour[rose_node->count] = rose_neigh; rose_node->count++; rose_neigh->count++; } + if (!rose_is_null(&rose_route->address)) + { + /* Delete this neighbourg from the dummy node 0000000000 */ + dummy_route = *rose_route; + dummy_route.mask = 10; + memset(&dummy_route.address, 0, sizeof(rose_address)); + rose_del_node(&dummy_route, dev); + } + return 0; } @@ -252,11 +270,11 @@ struct rose_route *s; unsigned long flags; - if (rose_route->neigh1 != NULL) - rose_route->neigh1->use--; + if (rose_route->tr1.neigh != NULL) + rose_route->tr1.neigh->use--; - if (rose_route->neigh2 != NULL) - rose_route->neigh2->use--; + if (rose_route->tr2.neigh != NULL) + rose_route->tr2.neigh->use--; save_flags(flags); cli(); @@ -290,6 +308,7 @@ { struct rose_node *rose_node; struct rose_neigh *rose_neigh; + struct rose_route_struct dummy_route; int i; for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) @@ -308,8 +327,16 @@ if (rose_node->neighbour[i] == rose_neigh) { rose_neigh->count--; - if (rose_neigh->count == 0 && rose_neigh->use == 0) + if (rose_neigh->count == 0) { + /* Add a dummy node to keep the neighbourg still visible */ + dummy_route = *rose_route; + dummy_route.mask = 10; + memset(&dummy_route.address, 0, sizeof(rose_address)); + rose_add_node(&dummy_route, dev); + } + /* if (rose_neigh->count == 0 && rose_neigh->use == 0) { rose_remove_neigh(rose_neigh); + } */ rose_node->count--; @@ -388,7 +415,7 @@ s = rose_route; rose_route = rose_route->next; - if (s->neigh1->dev == dev || s->neigh2->dev == dev) + if (s->tr1.neigh->dev == dev || s->tr2.neigh->dev == dev) rose_remove_route(s); } } @@ -472,8 +499,8 @@ struct rose_route *rose_route; for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) - if ((rose_route->neigh1 == neigh && rose_route->lci1 == lci) || - (rose_route->neigh2 == neigh && rose_route->lci2 == lci)) + if ((rose_route->tr1.neigh == neigh && rose_route->tr1.lci == lci) || + (rose_route->tr2.neigh == neigh && rose_route->tr2.lci == lci)) return rose_route; return NULL; @@ -496,7 +523,7 @@ else failed = 1; } - /* F6FBB : All nodes for this route are out of order */ + /* do not accept other node address */ break; } } @@ -529,8 +556,8 @@ memcpy_fromfs(&rose_route, arg, sizeof(struct rose_route_struct)); if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) return -EINVAL; - if (rose_dev_get(&rose_route.address) != NULL) /* Can't add routes to ourself */ - return -EINVAL; + /* if (rose_dev_get(&rose_route.address) != NULL) / Can't add routes to ourself / + return -EINVAL; */ if (rose_route.mask > 10) /* Mask can't be more than 10 digits */ return -EINVAL; @@ -571,25 +598,25 @@ rose_route = rose_route_list; while (rose_route != NULL) { - if ((rose_route->neigh1 == rose_neigh && rose_route->neigh2 == rose_neigh) || - (rose_route->neigh1 == rose_neigh && rose_route->neigh2 == NULL) || - (rose_route->neigh2 == rose_neigh && rose_route->neigh1 == NULL)) { + if ((rose_route->tr1.neigh == rose_neigh && rose_route->tr2.neigh == rose_neigh) || + (rose_route->tr1.neigh == rose_neigh && rose_route->tr2.neigh == NULL) || + (rose_route->tr2.neigh == rose_neigh && rose_route->tr1.neigh == NULL)) { s = rose_route->next; rose_remove_route(rose_route); rose_route = s; continue; } - if (rose_route->neigh1 == rose_neigh) { - rose_route->neigh1->use--; - rose_route->neigh1 = NULL; - rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0); + if (rose_route->tr1.neigh == rose_neigh) { + rose_route->tr1.neigh->use--; + rose_route->tr1.neigh = NULL; + rose_transmit_clear_request(rose_route->tr2.neigh, rose_route->tr2.lci, ROSE_OUT_OF_ORDER, 0); } - if (rose_route->neigh2 == rose_neigh) { - rose_route->neigh2->use--; - rose_route->neigh2 = NULL; - rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0); + if (rose_route->tr2.neigh == rose_neigh) { + rose_route->tr2.neigh->use--; + rose_route->tr2.neigh = NULL; + rose_transmit_clear_request(rose_route->tr1.neigh, rose_route->tr1.lci, ROSE_OUT_OF_ORDER, 0); } rose_route = rose_route->next; @@ -634,13 +661,23 @@ } /* + * A Restart has occured. Delete all existing routes to this neighbour + */ +void rose_clean_neighbour(struct rose_neigh *rose_neigh) +{ + rose_del_route_by_neigh(rose_neigh); + rose_kill_by_neigh(rose_neigh); +} + + +/* * Route a frame to an appropriate AX.25 connection. */ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) { struct rose_neigh *rose_neigh, *new_neigh; struct rose_route *rose_route; - struct rose_facilities facilities; + struct rose_facilities_struct facilities; rose_address *src_addr, *dest_addr; struct sock *sk; unsigned short frametype; @@ -648,6 +685,8 @@ unsigned char cause, diagnostic; struct device *dev; unsigned long flags; + int len; + struct sk_buff *skbn; #ifdef CONFIG_FIREWALL if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT) @@ -663,8 +702,10 @@ if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->device == rose_neigh->dev) break; - if (rose_neigh == NULL) + if (rose_neigh == NULL) { + printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr)); return 0; + } /* * Obviously the link is working, halt the ftimer. @@ -684,16 +725,36 @@ * Find an existing socket. */ if ((sk = rose_find_socket(lci, rose_neigh)) != NULL) { - skb->h.raw = skb->data; - return rose_process_rx_frame(sk, skb); + if (frametype == ROSE_CALL_REQUEST) + { + /* F6FBB - Remove an existing unused socket */ + rose_clear_queues(sk); + sk->protinfo.rose->cause = ROSE_NETWORK_CONGESTION; + sk->protinfo.rose->diagnostic = 0; + sk->protinfo.rose->neighbour->use--; + sk->protinfo.rose->neighbour = NULL; + sk->protinfo.rose->state = ROSE_STATE_0; + sk->state = TCP_CLOSE; + sk->err = 0; + sk->shutdown |= SEND_SHUTDOWN; + if (!sk->dead) + sk->state_change(sk); + sk->dead = 1; + } + else + { + skb->h.raw = skb->data; + return rose_process_rx_frame(sk, skb); + } } /* - * Is is a Call Request and is it for us ? + * Is it a Call Request and is it for us ? */ if (frametype == ROSE_CALL_REQUEST) - if ((dev = rose_dev_get(dest_addr)) != NULL) + if ((dev = rose_dev_get(dest_addr)) != NULL) { return rose_rx_call_request(skb, dev, rose_neigh, lci); + } if (!sysctl_rose_routing_control) { rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); @@ -704,12 +765,20 @@ * Route it to the next in line if we have an entry for it. */ for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) { - if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) { - if (rose_route->neigh2 != NULL) { + if (rose_route->tr1.lci == lci && rose_route->tr1.neigh == rose_neigh) { + if (frametype == ROSE_CALL_REQUEST) { + /* F6FBB - Remove an existing unused route */ + rose_remove_route(rose_route); + break; + } else if (rose_route->tr2.neigh != NULL) { + if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) { + kfree_skb(skb, FREE_READ); + skb = skbn; + } skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; - rose_transmit_link(skb, rose_route->neigh2); + skb->data[0] |= (rose_route->tr2.lci >> 8) & 0x0F; + skb->data[1] = (rose_route->tr2.lci >> 0) & 0xFF; + rose_transmit_link(skb, rose_route->tr2.neigh); if (frametype == ROSE_CLEAR_CONFIRMATION) rose_remove_route(rose_route); return 1; @@ -719,12 +788,20 @@ return 0; } } - if (rose_route->lci2 == lci && rose_route->neigh2 == rose_neigh) { - if (rose_route->neigh1 != NULL) { + if (rose_route->tr2.lci == lci && rose_route->tr2.neigh == rose_neigh) { + if (frametype == ROSE_CALL_REQUEST) { + /* F6FBB - Remove an existing unused route */ + rose_remove_route(rose_route); + break; + } else if (rose_route->tr1.neigh != NULL) { + if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) { + kfree_skb(skb, FREE_READ); + skb = skbn; + } skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci1 >> 0) & 0xFF; - rose_transmit_link(skb, rose_route->neigh1); + skb->data[0] |= (rose_route->tr1.lci >> 8) & 0x0F; + skb->data[1] = (rose_route->tr1.lci >> 0) & 0xFF; + rose_transmit_link(skb, rose_route->tr1.neigh); if (frametype == ROSE_CLEAR_CONFIRMATION) rose_remove_route(rose_route); return 1; @@ -741,10 +818,17 @@ * 1. The frame isn't for us, * 2. It isn't "owned" by any existing route. */ - if (frametype != ROSE_CALL_REQUEST) /* XXX */ + if (frametype != ROSE_CALL_REQUEST) { /* Trashed packet */ return 0; + } - if (!rose_parse_facilities(skb, &facilities)) { + len = (((skb->data[3] >> 4) & 0x0F) + 1) / 2; + len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2; + + memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); + + if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { + printk("CR : rose_parse_facilities error\n"); rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); return 0; } @@ -757,16 +841,12 @@ rosecmp(src_addr, &rose_route->src_addr) == 0 && ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 && ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { - printk(KERN_DEBUG "ROSE: routing loop from %s\n", rose2asc(src_addr)); - printk(KERN_DEBUG "ROSE: to %s\n", rose2asc(dest_addr)); rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); return 0; } } if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) { - if (cause == ROSE_NOT_OBTAINABLE) - printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr)); rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); return 0; } @@ -777,33 +857,38 @@ } if ((rose_route = (struct rose_route *)kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) { - rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 121); return 0; } - rose_route->lci1 = lci; rose_route->src_addr = *src_addr; rose_route->dest_addr = *dest_addr; rose_route->src_call = facilities.dest_call; rose_route->dest_call = facilities.source_call; rose_route->rand = facilities.rand; - rose_route->neigh1 = rose_neigh; - rose_route->lci2 = new_lci; - rose_route->neigh2 = new_neigh; + rose_route->tr1.lci = lci; + rose_route->tr1.neigh = rose_neigh; + rose_route->tr2.lci = new_lci; + rose_route->tr2.neigh = new_neigh; - rose_route->neigh1->use++; - rose_route->neigh2->use++; + rose_route->tr1.neigh->use++; + rose_route->tr2.neigh->use++; save_flags(flags); cli(); rose_route->next = rose_route_list; rose_route_list = rose_route; restore_flags(flags); + if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) { + kfree_skb(skb, FREE_READ); + skb = skbn; + } + skb->data[0] &= 0xF0; - skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F; - skb->data[1] = (rose_route->lci2 >> 0) & 0xFF; + skb->data[0] |= (rose_route->tr2.lci >> 8) & 0x0F; + skb->data[1] = (rose_route->tr2.lci >> 0) & 0xFF; - rose_transmit_link(skb, rose_route->neigh2); + rose_transmit_link(skb, rose_route->tr2.neigh); return 1; } @@ -920,22 +1005,22 @@ len += sprintf(buffer, "lci address callsign neigh <-> lci address callsign neigh\n"); for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) { - if (rose_route->neigh1 != NULL) { + if (rose_route->tr1.neigh != NULL) { len += sprintf(buffer + len, "%3.3X %-10s %-9s %05d ", - rose_route->lci1, + rose_route->tr1.lci, rose2asc(&rose_route->src_addr), ax2asc(&rose_route->src_call), - rose_route->neigh1->number); + rose_route->tr1.neigh->number); } else { len += sprintf(buffer + len, "000 * * 00000 "); } - if (rose_route->neigh2 != NULL) { + if (rose_route->tr2.neigh != NULL) { len += sprintf(buffer + len, "%3.3X %-10s %-9s %05d\n", - rose_route->lci2, + rose_route->tr2.lci, rose2asc(&rose_route->dest_addr), ax2asc(&rose_route->dest_call), - rose_route->neigh2->number); + rose_route->tr2.neigh->number); } else { len += sprintf(buffer + len, "000 * * 00000\n"); } diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_subr.c linux/net/rose/rose_subr.c --- v2.0.35/linux/net/rose/rose_subr.c Sun Nov 15 10:50:02 1998 +++ linux/net/rose/rose_subr.c Sun Nov 15 10:33:22 1998 @@ -46,6 +46,36 @@ while ((skb = skb_dequeue(&sk->write_queue)) != NULL) kfree_skb(skb, FREE_WRITE); + + while ((skb = skb_dequeue(&sk->protinfo.rose->ack_queue)) != NULL) + kfree_skb(skb, FREE_WRITE); + +#ifdef M_BIT + while ((skb = skb_dequeue(&sk->protinfo.rose->frag_queue)) != NULL) + kfree_skb(skb, FREE_READ); +#endif +} + +/* + * This routine purges the input queue of those frames that have been + * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the + * SDL diagram. + */ +void rose_frames_acked(struct sock *sk, unsigned short nr) +{ + struct sk_buff *skb; + + /* + * Remove all the ack-ed frames from the ack queue. + */ + if (sk->protinfo.rose->va != nr) { + + while (skb_peek(&sk->protinfo.rose->ack_queue) != NULL && sk->protinfo.rose->va != nr) { + skb = skb_dequeue(&sk->protinfo.rose->ack_queue); + kfree_skb(skb, FREE_WRITE); + sk->protinfo.rose->va = (sk->protinfo.rose->va + 1) % ROSE_MODULUS; + } + } } /* @@ -75,7 +105,7 @@ struct sk_buff *skb; unsigned char *dptr; unsigned char lci1, lci2; - char buffer[100]; + char buffer[250]; int len, faclen = 0; len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1; @@ -83,14 +113,32 @@ switch (frametype) { case ROSE_CALL_REQUEST: len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN; + /* facilities */ faclen = rose_create_facilities(buffer, sk->protinfo.rose); len += faclen; break; case ROSE_CALL_ACCEPTED: - case ROSE_CLEAR_REQUEST: case ROSE_RESET_REQUEST: len += 2; break; + case ROSE_CLEAR_REQUEST: + len += 3; + /* facilities */ + faclen = 3 + 2 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN; + dptr = buffer; + *dptr++ = faclen-1; /* Facilities length */ + *dptr++ = 0; + *dptr++ = FAC_NATIONAL; + *dptr++ = FAC_NATIONAL_FAIL_CALL; + *dptr++ = AX25_ADDR_LEN; + memcpy(dptr, &rose_callsign, AX25_ADDR_LEN); + dptr += AX25_ADDR_LEN; + *dptr++ = FAC_NATIONAL_FAIL_ADD; + *dptr++ = ROSE_ADDR_LEN + 1; + *dptr++ = ROSE_ADDR_LEN * 2; + memcpy(dptr, &sk->protinfo.rose->source_addr, ROSE_ADDR_LEN); + len += faclen; + break; } if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) @@ -137,6 +185,9 @@ *dptr++ = frametype; *dptr++ = sk->protinfo.rose->cause; *dptr++ = sk->protinfo.rose->diagnostic; + *dptr++ = 0x00; /* Address length */ + memcpy(dptr, buffer, faclen); + dptr += faclen; break; case ROSE_RESET_REQUEST: @@ -144,7 +195,7 @@ *dptr++ = lci2; *dptr++ = frametype; *dptr++ = ROSE_DTE_ORIGINATED; - *dptr++ = 0; + *dptr++ = 0x00; /* Address length */ break; case ROSE_RR: @@ -209,9 +260,11 @@ return ROSE_ILLEGAL; } -static int rose_parse_national(unsigned char *p, struct rose_facilities *facilities, int len) +static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *facilities, int len) { - unsigned char l, n = 0; + unsigned char *pt; + unsigned char l, lg, n = 0; + int fac_national_digis_received = 0; do { switch (*p & 0xC0) { @@ -238,12 +291,33 @@ case 0xC0: l = p[1]; if (*p == FAC_NATIONAL_DEST_DIGI) { - memcpy(&facilities->source_digi, p + 2, AX25_ADDR_LEN); - facilities->source_ndigis = 1; + if (!fac_national_digis_received) { + memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN); + facilities->source_ndigis = 1; + } + } + else if (*p == FAC_NATIONAL_SRC_DIGI) { + if (!fac_national_digis_received) { + memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN); + facilities->dest_ndigis = 1; + } + } + else if (*p == FAC_NATIONAL_FAIL_CALL) { + memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN); + } + else if (*p == FAC_NATIONAL_FAIL_ADD) { + memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN); } - if (*p == FAC_NATIONAL_SRC_DIGI) { - memcpy(&facilities->dest_digi, p + 2, AX25_ADDR_LEN); - facilities->dest_ndigis = 1; + else if (*p == FAC_NATIONAL_DIGIS) { + fac_national_digis_received = 1; + facilities->source_ndigis = 0; + facilities->dest_ndigis = 0; + for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) { + if (pt[6] & AX25_HBIT) + memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); + else + memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN); + } } p += l + 2; n += l + 2; @@ -255,7 +329,7 @@ return n; } -static int rose_parse_ccitt(unsigned char *p, struct rose_facilities *facilities, int len) +static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *facilities, int len) { unsigned char l, n = 0; char callsign[11]; @@ -288,7 +362,7 @@ callsign[l - 10] = '\0'; facilities->source_call = *asc2ax(callsign); } - if (*p == FAC_CCITT_SRC_NSAP) { + else if (*p == FAC_CCITT_SRC_NSAP) { memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN); memcpy(callsign, p + 12, l - 10); callsign[l - 10] = '\0'; @@ -304,17 +378,9 @@ return n; } -int rose_parse_facilities(struct sk_buff *skb, struct rose_facilities *facilities) +int rose_parse_facilities(unsigned char *p, struct rose_facilities_struct *facilities) { int facilities_len, len; - unsigned char *p; - - memset(facilities, 0x00, sizeof(struct rose_facilities)); - - len = (((skb->data[3] >> 4) & 0x0F) + 1) / 2; - len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2; - - p = skb->data + len + 4; facilities_len = *p++; @@ -327,25 +393,25 @@ p++; switch (*p) { - case FAC_NATIONAL: /* National */ + case FAC_NATIONAL: /* National 0x00 */ len = rose_parse_national(p + 1, facilities, facilities_len - 1); facilities_len -= len + 1; p += len + 1; break; - case FAC_CCITT: /* CCITT */ + case FAC_CCITT: /* CCITT 0x0F */ len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); facilities_len -= len + 1; p += len + 1; break; default: - printk(KERN_DEBUG "rose_parse_facilities: unknown facilities family %02X\n", *p); facilities_len--; p++; break; } - } + } + else break; /* Error in facilities format */ } return 1; @@ -356,6 +422,7 @@ unsigned char *p = buffer + 1; char *callsign; int len; + int nb; /* National Facilities */ if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) { @@ -368,19 +435,43 @@ *p++ = (rose->rand >> 0) & 0xFF; } - if (rose->source_ndigis == 1) { + /* Sent before older facilities */ + if ((rose->source_ndigis > 0) || (rose->dest_ndigis > 0)) { + int maxdigi = 0; + *p++ = FAC_NATIONAL_DIGIS; + *p++ = AX25_ADDR_LEN * (rose->source_ndigis + rose->dest_ndigis); + for (nb = 0 ; nb < rose->source_ndigis ; nb++) { + if (++maxdigi >= ROSE_MAX_DIGIS) + break; + memcpy(p, &rose->source_digis[nb], AX25_ADDR_LEN); + p[6] |= AX25_HBIT; + p += AX25_ADDR_LEN; + } + for (nb = 0 ; nb < rose->dest_ndigis ; nb++) { + if (++maxdigi >= ROSE_MAX_DIGIS) + break; + memcpy(p, &rose->dest_digis[nb], AX25_ADDR_LEN); + p[6] &= ~AX25_HBIT; + p += AX25_ADDR_LEN; + } + } + + /* For compatibility */ + if (rose->source_ndigis > 0) { *p++ = FAC_NATIONAL_SRC_DIGI; *p++ = AX25_ADDR_LEN; - memcpy(p, &rose->source_digi, AX25_ADDR_LEN); + memcpy(p, &rose->source_digis[0], AX25_ADDR_LEN); p += AX25_ADDR_LEN; } - if (rose->dest_ndigis == 1) { + /* For compatibility */ + if (rose->dest_ndigis > 0) { *p++ = FAC_NATIONAL_DEST_DIGI; *p++ = AX25_ADDR_LEN; - memcpy(p, &rose->dest_digi, AX25_ADDR_LEN); + memcpy(p, &rose->dest_digis[0], AX25_ADDR_LEN); p += AX25_ADDR_LEN; } + } *p++ = 0x00; diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_timer.c linux/net/rose/rose_timer.c --- v2.0.35/linux/net/rose/rose_timer.c Sun Nov 15 10:50:02 1998 +++ linux/net/rose/rose_timer.c Sun Nov 15 10:33:23 1998 @@ -66,6 +66,7 @@ static void rose_timer(unsigned long param) { struct sock *sk = (struct sock *)param; + struct device *first; switch (sk->protinfo.rose->state) { case ROSE_STATE_0: @@ -82,7 +83,7 @@ /* * Check for the state of the receive buffer. */ - if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)) { + if (sk->rmem_alloc < ((sk->rcvbuf - ROSE_MAX_WINDOW_LEN) / 2) && (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)) { sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY; sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; sk->protinfo.rose->vl = sk->protinfo.rose->vr; @@ -120,11 +121,25 @@ case ROSE_STATE_1: /* T1 */ case ROSE_STATE_4: /* T2 */ rose_write_internal(sk, ROSE_CLEAR_REQUEST); + /* F6FBB - Disconnect the calling station sk->protinfo.rose->state = ROSE_STATE_2; sk->protinfo.rose->timer = sk->protinfo.rose->t3; - break; + break; */ + + /* Falls to next case */ case ROSE_STATE_2: /* T3 */ + /* F6FBB - Added Cause and diagnostic */ + sk->protinfo.rose->cause = ROSE_DTE_ORIGINATED; + sk->protinfo.rose->diagnostic = 0x30; + + /* F6FBB - Added Facilities */ + first = rose_dev_first(); + if (first) { + sk->protinfo.rose->facilities.fail_call = rose_callsign; + memcpy(&sk->protinfo.rose->facilities.fail_addr, first->dev_addr, ROSE_ADDR_LEN); + } + rose_clear_queues(sk); sk->protinfo.rose->neighbour->use--; sk->protinfo.rose->state = ROSE_STATE_0; diff -u --recursive --new-file v2.0.35/linux/net/socket.c linux/net/socket.c --- v2.0.35/linux/net/socket.c Mon Jul 13 13:46:43 1998 +++ linux/net/socket.c Sun Nov 15 10:33:23 1998 @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,7 @@ int size); static void sock_close(struct inode *inode, struct file *file); +static int sock_no_open(struct inode *inode, struct file *file); static int sock_select(struct inode *inode, struct file *file, int which, select_table *seltable); static int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); @@ -107,7 +109,7 @@ sock_select, sock_ioctl, NULL, /* mmap */ - NULL, /* no special open code... */ + sock_no_open, /* special open code... */ sock_close, NULL, /* no fsync */ sock_fasync @@ -216,22 +218,38 @@ * Go from a file number to its socket slot. */ -extern __inline struct socket *sockfd_lookup(int fd, struct file **pfile) +extern __inline struct socket *sockfd_lookup(int fd, int *err) { struct file *file; struct inode *inode; + struct socket *sock; - if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd])) + if (!(file = fget(fd))) + { + *err = -EBADF; return NULL; + } inode = file->f_inode; - if (!inode || !inode->i_sock) - return NULL; - if (pfile) - *pfile = file; + if (!inode || !inode->i_sock || !(sock = socki_lookup(inode))) + { + *err = -ENOTSOCK; + fput(file, inode); + return NULL; + } + + if (sock->file != file) { + printk(KERN_ERR "socki_lookup: socket file changed!\n"); + sock->file = file; + } + + return sock; +} - return socki_lookup(inode); +extern __inline__ void sockfd_put(struct socket *sock) +{ + fput(sock->file, sock->file->f_inode); } /* @@ -264,6 +282,7 @@ sock->wait = &inode->i_wait; sock->inode = inode; /* "backlink": we could use pointer arithmetic instead */ sock->fasync_list = NULL; + sock->file = NULL; sockets_in_use++; return sock; } @@ -279,6 +298,17 @@ sock_wake_async(peer, 1); } +/* + * In theory you can't get an open on this inode, but /proc provides + * a back door. Remember to keep it shut otherwise you'll let the + * creepy crawlies in. + */ + +static int sock_no_open(struct inode *inode, struct file *file) +{ + return -ENXIO; +} + void sock_release(struct socket *sock) { int oldstate; @@ -613,9 +643,10 @@ if ((fd1 = sys_socket(family, type, protocol)) < 0) return(fd1); - sock1 = sockfd_lookup(fd1, NULL); + sock1 = sockfd_lookup(fd1, &er); if (!sock1->ops->socketpair) { + sockfd_put(sock1); sys_close(fd1); return(-EINVAL); } @@ -626,14 +657,17 @@ if ((fd2 = sys_socket(family, type, protocol)) < 0) { + sockfd_put(sock1); sys_close(fd1); return(-EINVAL); } - sock2 = sockfd_lookup(fd2, NULL); + sock2 = sockfd_lookup(fd2, &er); if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) { + sockfd_put(sock1); sys_close(fd1); + sockfd_put(sock2); sys_close(fd2); return(i); } @@ -646,13 +680,17 @@ er=verify_area(VERIFY_WRITE, usockvec, sizeof(usockvec)); if(er) { + sockfd_put(sock1); sys_close(fd1); + sockfd_put(sock2); sys_close(fd2); return er; } put_user(fd1, &usockvec[0]); put_user(fd2, &usockvec[1]); + sockfd_put(sock1); + sockfd_put(sock2); return(0); } @@ -668,24 +706,20 @@ asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen) { struct socket *sock; - int i; char address[MAX_SOCK_ADDR]; int err; - if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) - return(-EBADF); - - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); + if (!(sock = sockfd_lookup(fd, &err))) + return err; if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0) - return err; + goto out; - if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0) - { - return(i); - } - return(0); + if ((err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) > 0) + err = 0; +out: + sockfd_put(sock); + return err; } @@ -700,20 +734,23 @@ struct socket *sock; int err=-EOPNOTSUPP; - if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); + if (!(sock = sockfd_lookup(fd, &err))) + return err; if (sock->state != SS_UNCONNECTED) - return(-EINVAL); - + { + err=-EINVAL; + goto out; + } + if (sock->ops && sock->ops->listen) { err=sock->ops->listen(sock, backlog); if(!err) sock->flags |= SO_ACCEPTCON; } +out: + sockfd_put(sock); return(err); } @@ -728,28 +765,23 @@ asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen) { - struct file *file; struct socket *sock, *newsock; int i; char address[MAX_SOCK_ADDR]; int len; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, &file))) - return(-ENOTSOCK); - if (sock->state != SS_UNCONNECTED) - { - return(-EINVAL); - } - if (!(sock->flags & SO_ACCEPTCON)) + if (!(sock = sockfd_lookup(fd, &i))) + return i; + if (sock->state != SS_UNCONNECTED || (!(sock->flags & SO_ACCEPTCON))) { + sockfd_put(sock); return(-EINVAL); } if (!(newsock = sock_alloc())) { printk(KERN_WARNING "accept: no more sockets\n"); + sockfd_put(sock); return(-ENOSR); /* Was: EAGAIN, but we are out of system resources! */ } @@ -758,19 +790,22 @@ if ((i = sock->ops->dup(newsock, sock)) < 0) { sock_release(newsock); + sockfd_put(sock); return(i); } - i = newsock->ops->accept(sock, newsock, file->f_flags); + i = newsock->ops->accept(sock, newsock, sock->file->f_flags); if ( i < 0) { sock_release(newsock); + sockfd_put(sock); return(i); } if ((fd = get_fd(SOCK_INODE(newsock))) < 0) { sock_release(newsock); + sockfd_put(sock); return(-EINVAL); } newsock->file=current->files->fd[fd]; @@ -780,6 +815,7 @@ newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1); move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen); } + sockfd_put(sock); return(fd); } @@ -792,18 +828,14 @@ asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen) { struct socket *sock; - struct file *file; - int i; char address[MAX_SOCK_ADDR]; int err; - if (fd < 0 || fd >= NR_OPEN || (file=current->files->fd[fd]) == NULL) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, &file))) - return(-ENOTSOCK); + if (!(sock = sockfd_lookup(fd, &err))) + return(err); if((err=move_addr_to_kernel(uservaddr,addrlen,address))<0) - return err; + goto out; switch(sock->state) { @@ -814,7 +846,8 @@ /* Socket is already connected */ if(sock->type == SOCK_DGRAM) /* Hack for now - move this all into the protocol */ break; - return -EISCONN; + err = -EISCONN; + goto out; case SS_CONNECTING: /* Not yet connected... we will check this. */ @@ -825,14 +858,15 @@ */ break; default: - return(-EINVAL); - } - i = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, file->f_flags); - if (i < 0) - { - return(i); + err = -EINVAL; + goto out; } - return(0); + err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, sock->file->f_flags); + if (err > 0) + err = 0; +out: + sockfd_put(sock); + return err; } /* @@ -847,17 +881,17 @@ int len; int err; - if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); + if (!(sock = sockfd_lookup(fd, &err))) + return err; err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 0); if(err) - return err; - if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))<0) - return err; - return 0; + goto out; + if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))>0) + err = 0; +out: + sockfd_put(sock); + return err; } /* @@ -872,17 +906,17 @@ int len; int err; - if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); + if (!(sock = sockfd_lookup(fd, &err))) + return err; err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 1); if(err) - return err; - if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))<0) - return err; - return 0; + goto out; + if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))>0) + err = 0; +out: + sockfd_put(sock); + return err; } /* @@ -893,29 +927,28 @@ asmlinkage int sys_send(int fd, void * buff, int len, unsigned flags) { struct socket *sock; - struct file *file; int err; struct msghdr msg; struct iovec iov; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); - if(len<0) return -EINVAL; err=verify_area(VERIFY_READ, buff, len); if(err) return err; + if (!(sock = sockfd_lookup(fd, &err))) + return err; + iov.iov_base=buff; iov.iov_len=len; msg.msg_name=NULL; msg.msg_iov=&iov; msg.msg_iovlen=1; msg.msg_control=NULL; - return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), flags)); + err=sock->ops->sendmsg(sock, &msg, len, (sock->file->f_flags & O_NONBLOCK), flags); + sockfd_put(sock); + return err; } /* @@ -928,22 +961,18 @@ struct sockaddr *addr, int addr_len) { struct socket *sock; - struct file *file; char address[MAX_SOCK_ADDR]; int err; struct msghdr msg; struct iovec iov; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); - if(len<0) return -EINVAL; err=verify_area(VERIFY_READ,buff,len); if(err) return err; + if (!(sock = sockfd_lookup(fd, &err))) + return err; iov.iov_base=buff; iov.iov_len=len; @@ -955,13 +984,17 @@ if (addr && addr_len) { err=move_addr_to_kernel(addr,addr_len,address); if (err < 0) + { + sockfd_put(sock); return err; + } msg.msg_name=address; msg.msg_namelen=addr_len; } - return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), - flags)); + err=sock->ops->sendmsg(sock, &msg, len, (sock->file->f_flags & O_NONBLOCK), flags); + sockfd_put(sock); + return err; } @@ -974,15 +1007,8 @@ struct iovec iov; struct msghdr msg; struct socket *sock; - struct file *file; int err; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); - if(size<0) return -EINVAL; if(size==0) @@ -990,6 +1016,8 @@ err=verify_area(VERIFY_WRITE, ubuf, size); if(err) return err; + if (!(sock = sockfd_lookup(fd, &err))) + return err; msg.msg_name=NULL; msg.msg_iov=&iov; @@ -998,7 +1026,9 @@ iov.iov_base=ubuf; iov.iov_len=size; - return(sock->ops->recvmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK), flags,&msg.msg_namelen)); + err=sock->ops->recvmsg(sock, &msg, size,(sock->file->f_flags & O_NONBLOCK), flags,&msg.msg_namelen); + sockfd_put(sock); + return err; } /* @@ -1011,24 +1041,20 @@ struct sockaddr *addr, int *addr_len) { struct socket *sock; - struct file *file; struct iovec iov; struct msghdr msg; char address[MAX_SOCK_ADDR]; int err; int alen; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); if(size<0) return -EINVAL; if(size==0) return 0; - err=verify_area(VERIFY_WRITE,ubuf,size); if(err) return err; + if (!(sock = sockfd_lookup(fd, &err))) + return err; msg.msg_control=NULL; msg.msg_iovlen=1; @@ -1037,9 +1063,11 @@ iov.iov_base=ubuf; msg.msg_name=address; msg.msg_namelen=MAX_SOCK_ADDR; - size=sock->ops->recvmsg(sock, &msg, size, (file->f_flags & O_NONBLOCK), + size=sock->ops->recvmsg(sock, &msg, size, (sock->file->f_flags & O_NONBLOCK), flags, &alen); + sockfd_put(sock); + if(size<0) return size; if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0) @@ -1056,14 +1084,14 @@ asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen) { struct socket *sock; - struct file *file; + int err; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); + if (!(sock = sockfd_lookup(fd, &err))) + return err; - return(sock->ops->setsockopt(sock, level, optname, optval, optlen)); + err=sock->ops->setsockopt(sock, level, optname, optval, optlen); + sockfd_put(sock); + return err; } /* @@ -1074,16 +1102,19 @@ asmlinkage int sys_getsockopt(int fd, int level, int optname, char *optval, int *optlen) { struct socket *sock; - struct file *file; + int err; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); - + if (!(sock = sockfd_lookup(fd, &err))) + return err; + if (!sock->ops->getsockopt) + { + sockfd_put(sock); return(0); - return(sock->ops->getsockopt(sock, level, optname, optval, optlen)); + } + err=sock->ops->getsockopt(sock, level, optname, optval, optlen); + sockfd_put(sock); + return err; } @@ -1094,14 +1125,13 @@ asmlinkage int sys_shutdown(int fd, int how) { struct socket *sock; - struct file *file; - - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); + int err; - return(sock->ops->shutdown(sock, how)); + if (!(sock = sockfd_lookup(fd, &err))) + return err; + err=sock->ops->shutdown(sock, how); + sockfd_put(sock); + return err; } /* @@ -1111,39 +1141,43 @@ asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned int flags) { struct socket *sock; - struct file *file; char address[MAX_SOCK_ADDR]; struct iovec iov[UIO_MAXIOV]; struct msghdr msg_sys; int err; int total_len; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); - - if(sock->ops->sendmsg==NULL) - return -EOPNOTSUPP; - - err=verify_area(VERIFY_READ, msg,sizeof(struct msghdr)); if(err) return err; + if (!(sock = sockfd_lookup(fd, &err))) + return err; + if(sock->ops->sendmsg==NULL) + { + err = -EOPNOTSUPP; + goto out; + } memcpy_fromfs(&msg_sys,msg,sizeof(struct msghdr)); /* do not move before msg_sys is valid */ if(msg_sys.msg_iovlen>UIO_MAXIOV) - return -EINVAL; + { + err = -EINVAL; + goto out; + } /* This will also move the address data into kernel space */ err = verify_iovec(&msg_sys, iov, address, VERIFY_READ); if (err < 0) - return err; + goto out; + total_len=err; - return sock->ops->sendmsg(sock, &msg_sys, total_len, (file->f_flags&O_NONBLOCK), flags); + err=sock->ops->sendmsg(sock, &msg_sys, total_len, (sock->file->f_flags&O_NONBLOCK), flags); +out: + sockfd_put(sock); + return err; } /* @@ -1153,7 +1187,6 @@ asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags) { struct socket *sock; - struct file *file; struct iovec iov[UIO_MAXIOV]; struct msghdr msg_sys; int err; @@ -1168,11 +1201,6 @@ struct sockaddr *uaddr; int *uaddr_len; - if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL)) - return(-EBADF); - if (!(sock = sockfd_lookup(fd, NULL))) - return(-ENOTSOCK); - err=verify_area(VERIFY_READ, msg,sizeof(struct msghdr)); if(err) return err; @@ -1180,6 +1208,10 @@ if(msg_sys.msg_iovlen>UIO_MAXIOV) return -EINVAL; + if (!(sock = sockfd_lookup(fd, &err))) + return err; + + /* * save the user-mode address (verify_iovec will change the * kernel msghdr to use the kernel address space) @@ -1188,22 +1220,31 @@ uaddr_len = &msg->msg_namelen; err=verify_iovec(&msg_sys,iov,addr, VERIFY_WRITE); if(err<0) - return err; + goto out; total_len=err; if(sock->ops->recvmsg==NULL) - return -EOPNOTSUPP; - len=sock->ops->recvmsg(sock, &msg_sys, total_len, (file->f_flags&O_NONBLOCK), flags, &addr_len); - if(len<0) - return len; + { + err = -EOPNOTSUPP; + goto out; + } + err=sock->ops->recvmsg(sock, &msg_sys, total_len, (sock->file->f_flags&O_NONBLOCK), flags, &addr_len); + if(err<0) + goto out; + len=err; if (uaddr != NULL) { err = move_addr_to_user(addr, addr_len, uaddr, uaddr_len); if (err) - return err; + goto out; } + sockfd_put(sock); return len; + +out: + sockfd_put(sock); + return err; } diff -u --recursive --new-file v2.0.35/linux/net/unix/garbage.c linux/net/unix/garbage.c --- v2.0.35/linux/net/unix/garbage.c Tue Mar 10 13:19:10 1998 +++ linux/net/unix/garbage.c Sun Nov 15 10:33:23 1998 @@ -39,6 +39,25 @@ * 2 of the License, or (at your option) any later version. * * Fixes: + * Al Viro 11 Oct 1998 + * Graph may have cycles. That is, we can send the descriptor + * of foo to bar and vice versa. Current code chokes on that. + * Fix: move SCM_RIGHTS ones into the separate list and then + * kfree_skb() them all instead of doing explicit fput's. + * Another problem: since fput() may block somebody may + * create a new unix_socket when we are in the middle of sweep + * phase. Fix: revert the logic wrt MARKED. Mark everything + * upon the beginning and unmark non-junk ones. + * + * [12 Oct 1998] AAARGH! New code purges all SCM_RIGHTS + * sent to connect()'ed but still not accept()'ed sockets. + * Fixed. Old code had slightly different problem here: + * extra fput() in situation when we passed the descriptor via + * such socket and closed it (descriptor). That would happen on + * each unix_gc() until the accept(). Since the struct file in + * question would go to the free list and might be reused... + * That might be the reason of random oopses on close_fp() in + * unrelated processes. * */ @@ -136,11 +155,11 @@ return in_stack == 0; } -extern inline void maybe_mark_and_push(unix_socket *x) +extern inline void maybe_unmark_and_push(unix_socket *x) { - if (x->protinfo.af_unix.marksweep&MARKED) + if (!(x->protinfo.af_unix.marksweep&MARKED)) return; - x->protinfo.af_unix.marksweep|=MARKED; + x->protinfo.af_unix.marksweep&=~MARKED; push_stack(x); } @@ -151,8 +170,9 @@ { static int in_unix_gc=0; unix_socket *s; - unix_socket *next; - + struct sk_buff *skb; + struct sk_buff_head hitlist; + /* * Avoid a recursive GC. */ @@ -170,16 +190,21 @@ } /* - * Assume everything is now unmarked + * Everything is now marked */ + for(s=unix_socket_list;s!=NULL;s=s->next) + { + s->protinfo.af_unix.marksweep|=MARKED; + } + /* Invariant to be maintained: - - everything marked is either: + - everything unmarked is either: -- (a) on the stack, or - -- (b) has all of its children marked - - everything on the stack is always marked + -- (b) has all of its children unmarked + - everything on the stack is always unmarked - nothing is ever pushed onto the stack twice, because: - -- nothing previously marked is ever pushed on the stack + -- nothing previously unmarked is ever pushed on the stack */ /* @@ -192,19 +217,19 @@ * If all instances of the descriptor are not * in flight we are in use. */ - if(s->socket && s->socket->file && s->socket->file->f_count > s->protinfo.af_unix.inflight) - maybe_mark_and_push(s); + if(s->socket && s->socket->file && + s->socket->file->f_count > s->protinfo.af_unix.inflight) + maybe_unmark_and_push(s); } /* - * Mark phase + * Mark phase */ while (!empty_stack()) { unix_socket *x = pop_stack(); unix_socket *f=NULL,*sk; - struct sk_buff *skb; tail: skb=skb_peek(&x->receive_queue); @@ -233,27 +258,38 @@ if((sk=unix_get_socket(*fp++))!=NULL) { /* - * Remember the first, mark the + * Remember the first, unmark the * rest. */ if(f==NULL) f=sk; else - maybe_mark_and_push(sk); + maybe_unmark_and_push(sk); } } } + /* + * If we are connecting we need to handle this too + */ + if(x->state == TCP_LISTEN) + { + if(f==NULL) + f=skb->sk; + else + maybe_unmark_and_push(skb->sk); + } skb=skb->next; } + /* * Handle first born specially */ if (f) { - if (!(f->protinfo.af_unix.marksweep&MARKED)) + if (f->protinfo.af_unix.marksweep&MARKED) { - f->protinfo.af_unix.marksweep|=MARKED; + f->protinfo.af_unix.marksweep&=~MARKED; x=f; f=NULL; goto tail; @@ -261,35 +297,44 @@ } } - /* - * Sweep phase. NOTE: this part dominates the time complexity - */ + skb_queue_head_init(&hitlist); - for(s=unix_socket_list;s!=NULL;s=next) + for(s=unix_socket_list;s!=NULL;s=s->next) { - next=s->next; - if (!(s->protinfo.af_unix.marksweep&MARKED)) + if (s->protinfo.af_unix.marksweep&MARKED) { - /* - * We exist only in the passing tree of sockets - * that is no longer connected to active descriptors - * Time to die.. - * - * Subtle item: We will correctly sweep out the - * socket that has just been closed by the user. - * We must not close this as we are in the middle - * of its close at this moment. Skip that file - * using f_count==0 to spot it. - */ - - if(s->socket && s->socket->file && s->socket->file->f_count) - close_fp(s->socket->file); + struct sk_buff *nextsk; + skb=skb_peek(&s->receive_queue); + while(skb && skb != (struct sk_buff *)&s->receive_queue) + { + nextsk=skb->next; + /* + * Do we have file descriptors ? + */ + if(*(int *)(skb->h.filp)) + { + /* + * Pull these buffers out of line + * so they will each be freed once + * at the end. + */ + skb_unlink(skb); + skb_queue_tail(&hitlist,skb); + } + skb=nextsk; + } } - else - s->protinfo.af_unix.marksweep&=~MARKED; /* unmark everything for next collection */ } - + + /* + * Here we are. Hitlist is filled. Die. + */ + + while ((skb=skb_dequeue(&hitlist))!=NULL) + { + kfree_skb(skb, FREE_READ); + } + in_unix_gc=0; - vfree(stack); } diff -u --recursive --new-file v2.0.35/linux/scripts/checkconfig.pl linux/scripts/checkconfig.pl --- v2.0.35/linux/scripts/checkconfig.pl Wed Dec 31 16:00:00 1969 +++ linux/scripts/checkconfig.pl Sun Nov 15 10:33:23 1998 @@ -0,0 +1,53 @@ +#! /usr/bin/perl +# +# checkconfig: find uses of CONFIG_* names without matching definitions. +# Copyright abandoned, 1998, Michael Elizabeth Chastain . + +use integer; + +$| = 1; + +foreach $file (@ARGV) +{ + # Open this file. + open(FILE, $file) || die "Can't open $file: $!\n"; + + # Initialize variables. + my $fInComment = 0; + my $fUseConfig = 0; + my $iLinuxConfig = 0; + my %configList = (); + + LINE: while ( ) + { + # Strip comments. + $fInComment && (s+^.*?\*/+ +o ? ($fInComment = 0) : next); + m+/\*+o && (s+/\*.*?\*/+ +go, (s+/\*.*$+ +o && ($fInComment = 1))); + + # Pick up definitions. + if ( m/^#/o ) + { + $iLinuxConfig = $. if m/^#\s*include\s*/o; + $configList{uc $1} = 1 if m/^#\s*include\s*/o; + $configList{$1} = 1 if m/^#\s*define\s+CONFIG_(\w*)/o; + $configList{$1} = 1 if m/^#\s*undef\s+CONFIG_(\w*)/o; + } + + # Look for usages. + next unless m/CONFIG_/o; + WORD: while ( m/\bCONFIG_(\w+)/og ) + { + $fUseConfig = 1; + last LINE if $iLinuxConfig; + next WORD if exists $configList{$1}; + print "$file: $.: need CONFIG_$1.\n"; + $configList{$1} = 0; + } + } + + # Report superfluous includes. + if ( $iLinuxConfig && ! $fUseConfig ) + { print "$file: $iLinuxConfig: not needed.\n"; } + + close(FILE); +} diff -u --recursive --new-file v2.0.35/linux/scripts/lxdialog/dialog.h linux/scripts/lxdialog/dialog.h --- v2.0.35/linux/scripts/lxdialog/dialog.h Thu Apr 18 04:38:54 1996 +++ linux/scripts/lxdialog/dialog.h Sun Nov 15 10:33:24 1998 @@ -28,6 +28,24 @@ #include CURSES_LOC +/* + * Colors in ncurses 1.9.9e do not work properly since foreground and + * background colors are OR'd rather than separately masked. This version + * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible + * with standard curses. The simplest fix (to make this work with standard + * curses) uses the wbkgdset() function, not used in the original hack. + * Turn it off if we're building with 1.9.9e, since it just confuses things. + */ +#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE) +#define OLD_NCURSES 1 +#undef wbkgdset +#define wbkgdset(w,p) /*nothing*/ +#else +#define OLD_NCURSES 0 +#endif + +#define TR(params) _tracef params + #define ESC 27 #define TAB 9 #define MAX_LEN 2048 diff -u --recursive --new-file v2.0.35/linux/scripts/lxdialog/lxdialog.c linux/scripts/lxdialog/lxdialog.c --- v2.0.35/linux/scripts/lxdialog/lxdialog.c Sun Aug 3 15:38:39 1997 +++ linux/scripts/lxdialog/lxdialog.c Sun Nov 15 10:33:24 1998 @@ -63,6 +63,9 @@ (void) setlocale (LC_ALL, ""); #endif +#ifdef TRACE + trace(TRACE_CALLS|TRACE_UPDATE); +#endif if (argc < 2) { Usage (argv[0]); exit (-1); diff -u --recursive --new-file v2.0.35/linux/scripts/lxdialog/menubox.c linux/scripts/lxdialog/menubox.c --- v2.0.35/linux/scripts/lxdialog/menubox.c Sat Apr 27 03:18:18 1996 +++ linux/scripts/lxdialog/menubox.c Sun Nov 15 10:33:24 1998 @@ -39,8 +39,12 @@ /* Clear 'residue' of last item */ wattrset (win, menubox_attr); wmove (win, choice, 0); +#if OLD_NCURSES for (i = 0; i < menu_width; i++) waddch (win, ' '); +#else + wclrtoeol(win); +#endif wattrset (win, selected ? item_selected_attr : item_attr); mvwaddstr (win, choice, item_x, menu_item); if (hotkey) { @@ -141,6 +145,7 @@ for (i = 0; i < width - 2; i++) waddch (dialog, ACS_HLINE); wattrset (dialog, dialog_attr); + wbkgdset (dialog, dialog_attr & A_COLOR); waddch (dialog, ACS_RTEE); if (title != NULL) { diff -u --recursive --new-file v2.0.35/linux/scripts/lxdialog/textbox.c linux/scripts/lxdialog/textbox.c --- v2.0.35/linux/scripts/lxdialog/textbox.c Tue Feb 6 23:47:28 1996 +++ linux/scripts/lxdialog/textbox.c Sun Nov 15 10:33:24 1998 @@ -90,6 +90,8 @@ /* Create window for text region, used for scrolling text */ text = subwin (dialog, height - 4, width - 2, y + 1, x + 1); + wattrset (text, dialog_attr); + wbkgdset (text, dialog_attr & A_COLOR); keypad (text, TRUE); @@ -101,6 +103,7 @@ for (i = 0; i < width - 2; i++) waddch (dialog, ACS_HLINE); wattrset (dialog, dialog_attr); + wbkgdset (dialog, dialog_attr & A_COLOR); waddch (dialog, ACS_RTEE); if (title != NULL) { @@ -459,8 +462,12 @@ getyx (win, y, x); /* Clear 'residue' of previous line */ +#if OLD_NCURSES for (i = 0; i < width - x; i++) waddch (win, ' '); +#else + wclrtoeol(win); +#endif } /* @@ -530,6 +537,7 @@ exit (-1); } wattrset (win, position_indicator_attr); + wbkgdset (win, position_indicator_attr & A_COLOR); percent = !file_size ? 100 : ((fpos - bytes_read + page - buf) * 100) / file_size; wmove (win, height - 3, width - 9);